Hrátky s Unicode identifkátory v Ruby

Na Silvestra vydal Jakub Vrána nevážně míněný článek o patchi, který umožňuje používat v PHP různé symboly z Unicode – třeba operátor nebo funkci . Vzhled programu se tak přibližuje matematickému zápisu.

Hned po přečtení textu jsem začal přemýšlet nad tím, jak něco podobného spáchat v Ruby. Ruby totiž povoluje Unicode znaky v identifikátorech, a není tak ani třeba upravovat interpret, jako bylo nutné v případě PHP.

Pro další čtení doporučuji nejdřív přečíst zmiňovaný Jakubův článek, abyste byli v obraze. Tento text z něj totiž svým uspořádáním vychází.

Konstanty

Definice hodnot s názvy jako ½ či ¼ není v Ruby problém:

½ = 0.5
¼ = 0.25
⅜ = 0.375
π = Math::PI

puts ½ # => 0.5
puts ¼ # => 0.25
puts ⅜ # => 0.375
puts π # => 3.14159265358979

Občas by se mohla hodit i prázdná množina nebo nekonečno:

∅ = []
∞ = 1.0 / 0.0

puts ∞ # => Infinity

Bohužel na rozdíl od opatchovaného PHP si tyto hodnoty nemůžeme definovat jako konstanty, neboť konstanty musí v Ruby začínat velkým písmenem. Musí postačit obyčejné proměnné.

Operátory

Psát v Ruby výrazy jako 1 ≠ 2 by bylo hezké, bohužel to ale tak úplně nejde, protože Ruby neumožňuje definovat vlastní operátory (z jazyků, co aspoň trochu znám, tohle umí jen Haskell). Pokud ale přežijeme volání "přes tečku", můžeme si vytvořit aliasy pro odpovídající metody na numerických třídách a volat pak je:

class Fixnum
  alias ≠ != # nefunguje v Ruby 1.8.x
  alias ≤ <=
  alias ≥ >=
  alias × *
  alias ÷ /
  # ...
end

# Analogicky i pro třídy Bignum a Float...

puts 1.≠ 2 # => true
puts 3.× 4 # => 12
puts 6.÷ 2 # => 2

Další matematika

Je libo odmocňovat pomocí funkce ? Stačí přidat alias do modulu Math:

module Math
  alias √ sqrt
end

Po inkluzi modulu už odmocninu můžeme používat:

include Math

puts √ 9 # => 3

Sumu () nebo součin () už jako alias definovat nemůžeme, protože Ruby nemá vhodné metody, na které by šly tyto názvy namapovat. Budeme si je tedy muset napsat (a to hezky funkcionálně :-)

def ∑(array)
  array.inject(0) { |sum, item| sum + item }
end

def ∏(array)
  array.inject(1) { |product, item| product * item }
end

puts ∑ [1,2,3,4] # => 10
puts ∏ [1,2,3,4] # => 24

Čistější by samozřejmě bylo definovat metody ve třídě Array, nebo ještě lépe v modulu Enumerable.

V Ruby 1.9.0 bychom díky vylepšené metodě Enumerable#inject mohli definice součtu a součinu ještě o trochu zkrátit:

def ∑(array)
  array.inject(0, :+)
end

def ∏(array)
  array.inject(1, :*)
end

Lambda

Pokud jste se nahlédli do seznamu novinek Ruby 1.9.0, možná jste si všimli nové syntaxe pro lambda funkce:

f = ->(a, b){ a + b }
puts f.call(1, 2) # => 3

Matzovi (tvůrci Ruby) znaky "->" údajně po menší rotaci a translaci připomínají písmenko "lambda". Jelikož mě ne, rozhodl jsem se lambdu nadefinovat po svém:

module Kernel
  alias λ lambda
end

f = λ { |a, b| a + b }
puts f.call(1, 2) # => 3

Pozor, nebezpečí!

V Ruby je (po vzoru Lispu) zvykem označovat metody, které "nebezpečně" modifikují obsah objektu, pomocí vykřičníku na konci jejich názvu:

s = "ABCD"
s.downcase!
puts s # => "abcd"

Pokud se vám to ale zdá málo výrazné, není problém použít jiný znak:

class String
  alias downcase☠ downcase!
  undef downcase!

  # Podobně s dalšími metodami...
end

s = "ABCD"
s.downcase☠
puts s # => "abcd"

Chcete si taky hrát?

Na případné hraní s Unicode doporučuji stáhnout čerstvé Ruby 1.9.0, které podporuje specifikaci kódování zdrojáku přímo v souboru – na první řádek stačí napsat například toto:

# encoding=utf-8

V Ruby 1.8.x Unicode identifikátory fungují taky, ale při spuštění programu, který je používá, je nutno použít volbu -Ku, která přepne Ruby do režimu UTF-8. Tato volba funguje i v Ruby 1.9.0.

Šup sem s Ruby 1.9.0

Ruby 1.9.0 je v tuto chvíli k dispozici jen jako balík se zdrojovým kódem, který je nutno rozbalit a zkompilovat.

Pokud to uděláte, tak při spouštění ./configure doporučuji použít volbu --prefix a specifikovat adresář, kam se Ruby 1.9.0 po zkompilování nainstaluje. Nehrozí tak, že by nově zkompilovaná verze přepsala verzi nainstalovanou v systému.

Na mém Ubuntu 7.10 proběhla kompilace Ruby 1.9.0 bez problémů a stačilo napsat variaci na obvyklou "svatou trojici" příkazů:

./configure --prefix=/home/dmajda/ruby19
make
sudo make install

Ke stažení

Příklady uvedené výše si můžete stáhnout hezky pohromadě v jednom souboru: unicode-fun.rb.

A to je konec...

alias † exit
†

Použité zdroje

Jan 8, 2008 – 21:47

Comments

Martin Hassman
[2] A je to vůbec správná úvaha? Je ∞ skutečně konstantní? Aneb platí v matematice vždy ∞ == ∞? Já měl za to že ne, matiku jsem už hodně dlouho neotevřel.

Pokud totiž není konstantní, tak to prostě nemůže být konstanta, to dá rozum, né? 8-)
Jakub Vrána
Aby nedošlo k nedorozumnění. Ten kód, který je uveden v mém článku, je funkční ve všech obvyklých verzích PHP. PHP totiž dovoluje v identifikátorech používat znaky z horní poloviny ASCII tabulky.

Zmiňovaný patch jenom problém řeší na jiné úrovni a hlavně přidává nové operátory, který v samotném PHP stejně jako v Ruby přidat nejdou.
zoul
[2] Některá nekonečna jsou „větší“ než jiná, ale co já vím, tak symbol ∞ se používá spíš v analýze (ve smyslu „jdi s tímhle číslem furt do pryč“) a na práci s nekonečny jsou „přesnější“ alefy, viz heslo Aleph_number na Wikipedii.

Funkci puts bychom mohli předělat třeba na ✏ :)

David Majda
[2] No jasně, není nekonečno jako nekonečno. Celých i reálných čísel je nekonečno, a přitom těch druhých je víc, než těch prvních.

A některá zdánlivě různá nekonečna jsou zas stejná: Třeba bodů v rovině je úplně stejně jako bodů v trojrozměrném prostoru, i když je jich zjevně víc.

Minimálně to první tvrzení je u nás na matfyzu látka z prváku :)

http://en.wikipedia.org/wiki/Infinity

[3] Dík za upřesnění.

Add comment

It is not possible to add comments to posts older than one month.