Ruby / functional style

Azzal foglalkoztam, hogy fejlesszem kicsit a funkcionális programozásom Ruby-ban. A népszerűsítéséért megosztok néhány példát.

http://paste.ubuntu.com/14120230/

# n hosszú véletlen jelszó generálása.

def abc; ("A".."Z").to_a + ("a".."z").to_a + ("0".."9").to_a end
def pass(n); n.times.map{abc.sample}.join end

pass 10
=> "FI2dbS9IcR"

# lottó számok

(1..90).to_a.sample(5).sort
=> [3, 7, 34, 73, 77]

# nevek sorba rendezése fordított alakjuk alapján
# és némely karakterük nagybetűvé alakítása véletlen módon

["besenyő", "boborján", "evetke"].sort_by{|x|x.reverse}\
.map{|x|x.split("").map{|x|rand(2) == 0 ? x : x.upcase}.join}
=> ["eVETke", "BoBorjáN", "beSEnYő"]

# számjegyek megszámlálása egy nagy véletlen számban

n = rand 1e100
x = n.to_s.split("").map(&:to_i).sort
puts n
(0..9).map{|y| puts "#{y} = #{x.count(y)}"}

11125214565316178251880560326334239156441905168599\
98640053592292797988571134482532464550895510824522
0 = 7
1 = 13
2 = 13
3 = 8
4 = 10
5 = 17
6 = 8
7 = 4
8 = 10
9 = 10

Hozzászólások

"In functional code, the output value of a function depends only on the arguments that are input to the function, so calling a function f twice with the same value for an argument x will produce the same result f(x) each time."

Ez innen: https://en.wikipedia.org/wiki/Functional_programming

Akárhogy értelmezem, ez azt jelenti, hogy a random nem játszik funkcionális programozásnál? Csak mert a példáid mindegyike tartalmaz véletlenszámon alapuló leválogatást/szűrést, stb.

A masik megoldas az lenne, ha adnal egy megfelelo hosszusagu, elore generalt random sorozatot inputnak. Igy ha ugyanazt megkapja kesobb is, ugyanaz lenne az eredmeny, es nem sertene azt az elvet.

--
Is that a banana in your pocket, or are you just happy to see me?
Neither, it's my new iPhone.

Ez a mondat így helytelen. Pure (as in: side-effect-free) functional programming esetén mondjuk azt, hogy f minden hívásakor ugyanazon x paraméterrel ugyanazt az eredményt adja.

Jó meglátás a random. Természetesen "játszik" (pure) funkcionális programozásnál is, viszont csak side-effect-et tartalmazó kódban. A Haskell szépen különválasztja a pure és impure részeket, értelemszerűen side-effect-tel operáló dolgokat csak impure kódrészletben futtathatsz. De nemcsak a randomszámgenerálás ilyen, hanem pl az I/O is, azaz hogy pl. a billentyűzetről beolvass. Szép is lenne, ha minden egyes "scanf()" visszatérési értéke mindig ugyanaz lenne! :-)

Nem tudom miert varnak egyesek csodat nehany funkcionalis nyelvi elemtol,
egy-egy olyan interpretertol ami nem kepes optimalizalni a funkciok es tipusok
tulajdonsagi alapjan.

Amit nem lehet megirni assemblyben, azt nem lehet megirni.

Ha az embernek ugyan annyi(vagy tobb) ido megerteni kodot mint nelkule akkor,
nincs sok haszna, egybkent lehet jo.

Amit en nem szeretek az funcionalis nyelvi elemek kuruli hype,
funkcionals nyelvek pl. attol lehetnek jobbak `a hagyomanyostol`,
hogy `jobb` kodot lehetnek kepesek generalni, mint amit te leirtal,
ill. olyan tulajdonsagok menten is optimalizalhatnak amit mas eszkozoknel
nem lehetseges, mert az eszkoz szamara nem tudhato, hogy az adott dolog biztonsagosan
meglepheto ott es akkor.

perl/(c)python/(c)ruby nem kepes erre, butak mint a fold :)

Amit nem lehet megirni assemblyben, azt nem lehet megirni.

A hype nem érdekel, hanem az, hogy mennyire tudok még produktívabb lenni. A fenti példák sokkal jobbak, mint ha ciklusokkal meg sok if / then elágazásokkal valósítanád meg ruby-ban vagy másban.

Ha belemerülsz kicsit Ruby-ba, akkor a fenti példákat balról jobbra ki lehet olvasni angolul, például:

(1..90).to_a.sample(5).sort

numbers from 1 to 90, convert to array, take a sample of 5 and sort it.

Írd meg ezt áttekinthetőbben, egyszerűbben és hiba mentesebben nekem bármilyen nyelven.

Ha nagy véletlen szám kell és ebből több, akkor arra gondoltam, hogy számjegyenként rakom össze jelen esetben az 5 darab számot úgy, hogy az azonos decimális helyen nem egyezhetnek a jegyeik. Ehhez az 5 szám minden egyes decimális helyéhez a (0..9) listából veszek 5 random nem egyező számot a sample metódussal.

/ Megjegyzés: (0..9) megegyezik 10.times -szal/

(0..9).to_a.sample(5)
=> [8, 0, 7, 1, 2]

10.times.map{10.times.to_a.sample(5)}
=> [[8, 0, 7, 1, 2],
[3, 7, 9, 8, 1],
[8, 6, 1, 0, 5],
[1, 5, 0, 4, 7],
[0, 8, 3, 1, 2],
[9, 1, 0, 7, 4],
[1, 6, 8, 4, 5],
[2, 3, 1, 7, 9],
[9, 6, 2, 8, 1],
[8, 9, 5, 4, 2]]

Ezzel egy olyan tömböt kaptam, amelyben a számjegyeket függőlegesen kellene összerakni. Tehát az 5 számból az első ez:

"8381091298"

Ezen segít a transpose:

10.times.map{10.times.to_a.sample(5)}.transpose.map(&:join).map(&:to_i)
=> [8381091298, 765816369, 7910308125, 1804174784, 2157245912]

Mivel egy szám kezdődhet több nullával is, így ezzel megoldott az, hogy ne csak pont 10 számjegyből álló számot kapjunk. Vagyis bármilyen nagyságú számot kaphatunk nulla és 10**10 között. Ha megnézed például, a másik szám eggyel kevesebb jegyű.

Igaz, a 10-nél több szám előállítása ezzel nem megy. De te nem is ezt kérdezted. Ezt már rád bízom ;)

(Sebesség: 100 ezer számot 1.4 sec alatt generál Core2Duo-s gépem)