Ruby jegyzet

 ( log69 | 2013. március 4., hétfő - 23:25 )

Lambda megoldás Ruby-ban, vagyis sorba rendezés többszörös feltétel alapján (sajnos elég pocsék a Ruby dokumentáció és nem látom dokumentálva, 1.8.7+ verziókkal tesztelve és működik):

Array.sort_by{|var| [cond1, cond2, cond3, ..., condn-1, condn]}

Példa:

a = (1..10).map{|x| x = rand(2000)+10}
a.sort_by{|x| [x.to_s[1], x.to_s.length, x]}

Vagyis veszünk pár darab véletlen számot és sorba rendezzük őket elsődlegesen a második karakter alapján, másodlagosan a szám karaktereinek száma (string hossz), harmadsorban pedig a szám nagysága alapján.

Színezve a pastebin-en itt.

Megjegyzés: Mintha a Ruby 2.0 megjelenésével kapcsolatban olvastam volna, hogy rendbe fogják szedni a dokumentációt. Ez mindenképp jót tenne a projektnek és remélem hogy sok munkát tolnak majd bele.

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.

Nem ertem, mi a baj ezzel a dokumentacioval.
--
Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal

Nem látom többszörös feltételek megadásának lehetőségét.

Mert itt valojaban nem tobbszoros feltetelekrol van szo. Sot, valojaban csak egy darab feltetelt adsz meg.

Amirol itt szo van, az a tomb alapjan torteno rendezes. Amit a te kifejezesed visszaad, az egy darab tomb - azaz egy darab feltetel. Viszont rubyban tomboket is lehet rendezni, megpedig ugy, hogy a sort_by sorra veszi a tombelemeket, es az alapjan rendez. Vagyis eloszor az elso tombelem alapjan, masodszor a masodik alapjan... es igy tovabb. Ez nem csak a sort_by -al mukodik, hanem minden hasonlo jellegu metodussal, akkor is, ha en most egyet sem tudok felsorolni.

De peldaul ha van egy hashed, amibol tombok tombjet keszited (az elemek a [kulcs, ertek]) akkor azt is ugyanigy lehet rendezni egy sima Enumerable#sort -tal (a hashek egyebkent igy is rendezodnek, egyetlen fajdalmas problema, hogy a vegen a #sort az nem Hash-t, hanem Array-t ad vissza, vagyis ezt meg vissza kell alakitani hash-e - es ezert tilos a #sort! hasznalata hasheken.).

Ez persze virtualisan tobbszoros feltetelmegadasnak latszik - viszont valojaban ennel kicsit kevesebbrol es egy kicsit tobbrol van szo.
--
Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal

Nem több dimenziós tömbről van szó. Éppen ez a lényeg. Ha megnézed, a sorba rendezendő adatom egy 1 dimenziós tömb.

Az általad linkelt doksi alapján nem igazán látszik, hogy a sort_by feltételes részénél magának a feltételnek használhatnék több elemből álló tömböt benne a feltételekkel - és így elérem a célom. A doksi szépen szemlélteti az 1 feltétel alapján való rendezést, vagy hash-eknél több azt annyi.

Nem értem minek keverted ide a hash-t, nincs köze a fenti dolog működéséhez és nem is szemlélteti jobban.

Szerk.: egyébként értem hogy arra célzol, hogy ennek magától értetődőnek kellene lennie. Viszont abszolút nem az. A több feltétel alapján való rendezés külön igény és a doksi szerintem mindenképpen jobb lenne, ha kitérne rá.

Meg mindig nem erted. 1 tomb = 1 feltetel.

[[1,2],[1,3]].sort_by { |tombocske| }

Ez egy tobb dimenzios tomb rendezesenek vaza. A tombocske maga egy tombelem lesz, e szerint rendezunk. De ez meg mindig egy darab feltetel, noha valojaban egy ketelemu tombunk van.

%w{alma korte szilva banan}.sort_by { |gyumolcs| [gyumolcs[0], gyumolcs.size] }

Ez egy egy dimenzios tomb rendezese. A gyumolcs valtozoban egy darab string van, megis, mi egy darab tombot adunk vissza mint rendezesi szempont. A tombon belul viszont ket - a te terminologiaddal elve - "feltetelt". De ettol ez valojaban csak 1 darab feltetel, akarmit is mondasz. Mindossze annyi van, hogy lehetseges tombok szerint is rendezni.

Hogy jobban megertsd, ez valojaban valami ilyesmire fordul le:

%w{alma korte szilva banan}.map { |gyumolcs| [gyumolcs[0], gyumolcs.size] }.sort

Csak a vegen nem a rendezo tombot kapod meg (amit itt a #sort visszaadna), hanem az eredeti tomb rendezett verziojat (a keletkezo tombelemek pedig csak belul kerulnek felhasznalasra).

Meg idepasztazom ezt is, hatha segit (szinten ekvivalens)

%w{alma korte szilva banan}.sort { |gyum1, gyum2| [gyum1[0], gyum1.size] <=> [gyum2[0], gyum2.size] }

Itt valojaban szinten ugyanerrol van szo: ket gyumolcsot ugy hasonlitunk ossze, hogy vesszuk az elso karakteret, es vesszuk a meretet. Meg mindig egyetlen szempont: egy tomb alapjan rendezunk, a tomb tartalma azonban specialis. Lehetne peldaul ilyen rendezest is csinalni:

%w{alma korte szilva banan}.sort { |gyum1, gyum2| [6, 42] <=> [42,44] }
%w{alma korte szilva banan}.sort_by { |gyumolcs| [40, 42] }

Es ezek a tombok mar nem feltetelek tombje, csak ket szam tombje. Vagy?

Egyebkent olyan szempontbol valoban nem egyertelmu, hogy a tombok rendezhetosege nem szokvanyos dolog (bar ugy remlik, Javaban peldaul a kollekciok implementaljak a comparable interfeszt, de erre nem mernek fogadni is). A tobbi azonban - ha az ember tudja, hogy a tombok osszehasonlithatoak - mar trivialis.
--
Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal

"..lehetseges tombok szerint is rendezni..."

Ezt írtam a bejegyzésben. Még mindig nem látom az érveidet arra, hogy miért egyértelmű egy 1 vagy több dimenziós tömböt olyan feltétellel rendezni, amely szintén tömb? A dokumentáció mely részéből lehet ezt ilyen nyilvánvalóan kikövetkeztetni, mint amennyire te próbálod most bemutatni?

En szemely szerint ebbol jottem ra, illetve sokaig en a sima #sort -ot hasznaltam, a #sort_by -rol csak kesobb olvastam el, mire valo.
--
Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal

Szerintem ennek nincs köze hozzá.

?!

Nem tudom azóta rájöttél-e, de tényleg ettől működik ez az egész. Ez nem a sort_by trükkje, hanem az Array-jé, ahogy hrgy84 már kifejtette.

Talán magad is összezavarod azzal, hogy "feltételről" beszélsz: a sort_by nem feltételt* vár,
hanem egy objektumot, ami összehasonlítható, ráadásul nem ő fogja elvégezni az összehasonlítást, hanem az objektum maga (adott esetben Array) a <=>() metódusával - ezért az ő trükkje ez, nem a sort_by -jé.

*feltéte(l)t mi várunk a sárgaborsófőzelékre. basszus, vacsoráznom kéne már.. :)

"..If any value isn’t equal, then that inequality is the return value..."

Ezt hogy hozod össze a sorba rendezendő, több elemből álló 1 dimenziós tömbbel és a feltétellel, ami szintén egy több elemből álló tömb úgy, hogy a sort_by ne támogassa külön ezt? Ráadásul úgy, hogy a feltételek sorrendje számít! Tehát a végeredmény sorba lesz rendezve, elsődlegesen az első, aztán a második feltétel alapján és így tovább.

Szívesen veszek Ruby kód példát, addig számomra nem egyértelmű az általam leírt működés - sem a sort_by doksi alapján, sem a comparison alapján. Mert ugye ez a téma apropója, hogy szerintem nem egyértelmű és jobb lenne dokumentálva. Mire ti azt írjátok, hogy ez bizony nagyon egyértelmű.

Nem vagyok meggyőzve.

Ugye azt is mondod a fentivel, hogy már belefutottál olyan problémába, hogy több feltétel alapján akartál rendezni adatot, majd ránéztél a sort_by doksira és az általam leírt fenti megoldással álltál elő? Ha igen, akkor nincs miről beszélni, mert akkor tudás különbség van köztünk a javadra. Ha nem, akkor mire a hsz?

"Each object in each array is compared (using <=>). If any value isn’t equal, then that inequality is the return value."
Tehát: minden objektum össze lesz hasonlítva a tömbökben, ha valamely érték nem egyenlő, akkor az az egyenlőtlenség lesz a visszatérési érték.

Mit csinál a sort_by? "Sorts enum using a set of keys generated by mapping the values in enum through the given block." Leegyszerűsítve tehát két dolgot:
1. minden elemre meghívja a blokkot és annak a visszatérési értékét (objektumát) rögzíti az adott elemhez, amit majd a rendezésnél használ fel az elem helyett ("The current implementation of sort_by generates an array of tuples containing the original collection element and the mapped value.")
2. rendez

Nem tudom, hogy mit értessz félre, emiatt megpróbálom lépésről lépésre lebontani mi történik, hátha te magad rájössz, hogy hol gondolkodsz másképp.

Szóval a sort_by-ban vagyunk, rendezésnél, fogunk két elemet és a rájuk/velük korábban meghívott blokk visszatérési értékét összehasonlítjuk, hogy kiderüljön melyk a nagyobb, tehát: [0,1,0] <=> [0,0,0] - mi történik itt?

Ahogy a doksi mondja (Each object in each array is compared), 3 potenciális összehasonlítás van függőben:
1. 0 <=> 0
2. 1 <=> 0
3. 0 <=> 0

Az 1. egyenlő lesz, tehát az Array <=>() metódusa lép a következőre.
A 2. egyenlőtlen lesz, tehát ahogy a doksi mondja (If any value isn’t equal), az Array <=>() metódusa befejezi az összehasonlítást és visszatér azzal, hogy az első array nagyobb, mint a másik (that inequality is the return value). A sort_by ezen eredménynek hatására az első elemet hátrébb sorolja mint a másikat, hiszen "nagyobb".
A 3. összehasonlítás már nem fut le.

Tudtam az array <=> metódusáról, mert már többször beleütköztem a doksiba és bevésődött, hogy van ilyen. Ha pedig kipróbálom:

irb(main):001:0> [0,1]<=>[0,0]
=> 1
irb(main):002:0> [0,0]<=>[0,1]
=> -1

már gyanítom is, hogy mire jó, a doksi pedig megerősíti a gyanút és megvan a megoldás.

Nem a doksival van baj, hanem valahol még nem rubyban gondolkodsz, de tényleg nem tudom, hogy hol. De nincs ezzel semmi gond, csak próbáld ezt a megoldást megérteni, mert ez egy nagyon tipikus rubys megoldás.

Végülis a sort_by -nál tényleg lehetne ez egy példa, de igazából az a gond, hogy nem biztos, hogy az alapján bárki megérti, hogy mitől működik, ha pedig fogalom nélkül használja, előbb utóbb pofonba szalad..

En pedig siman csak elnezest kell kerjek. Nem vagyok egy pedagogus alkat, igy keptelen lettem volna ilyen melysegben elmagyarazni.

Egyebkent valoban errol van szo.
--
Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal

Köszönöm hogy próbálod elmagyarázni, de még mindig nem értem. Had kérdezzek:

A példát most ne két darab 3 elemű tömbbel nézzük, hanem az alábbival. Rendezendő tömb:

[15, 1, 0, 5, 14, 7, 10, 4]

A sorba rendezési feltétel:

{|x| [x.to_s.length, x]}

Itt a tömbben 2 elem van, ezt hogy hasonlítja össze a 8 elemes adattal? Mert ha az adatból mindig 1 elemet vesz, a feltételnél pedig veszi a 2 elemet, akkor a <=> comparison-nál mindig csak az első elem hasonlítása tud megtörténni - ergó felesleges a feltételnél a második elem. Vagy az adatból annyit vesz mindig, amennyi elem a feltétel tömbjében van? Ha igen, akkor sem értem hogy tudja így létrehozni minden elem mellé a kulcsot (vagyis a végleges helyen való sorszámát), ami alapján ki tudja printelni a végén a kért sorrendben az adat elemeket.

A sort_by metódust algoritmizálva és leprogramozva tudnám igazán megérteni. Le tudnád programozni esetleg pár sorban hogyan működik?

ugy, hogy a 8 elemu tombodbol egy ilyen tombot csinal a hatterben (errol te nem tudsz):

[
  [15, [2, 15],
  [1, [1, 1],
  [0, [1, 0],
  ...
]

Majd vegig megy az igy letrejovo tomb elemein parosaval. Az egyes elemtombok elso eleme maga a szam, aminek a helyet keressuk, de a ket elem osszehasonlitasat a segedtombokkel (az elso ketto eseteben a [2,15] es a [1,1] tombok, amit az altalad megadott kifejezes ad egyebkent vissza) osszehasonlitasaval vegzi, es az itt kialakult vegeredmenyt (egyenlo, kisebb, nagyobb) hasznalja fel az EREDETI tomb elemeinek rendezese soran (hogy hova rakja oket).
--
Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal

Úgy látszik, merőben máshogy gondolkodtam, mert most értem. Igazából nem is kellene idekeverni a comparison operátort. Annyit csinál a sort_by, hogy behelyettesíti a változó értékekeit az adattal, és ahogy írod, az így létrejövő több dimenziós tömböt rendezi.

Így már abszolút érthető a működése és a feltételeknek használt tömb. A fenti egymásba ágyazott tömbös példád szemlélteti tökéletesen.

Nahát, ez nem esett le. Nem tudom miért, de komplexabb belső működésre gondoltam.

Tudod, KISS.
--
Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal

Egy haszna mindenképp van, nem tudtam hogy ez helyett:

(1..100).map{|x| x = rand(100)}

...ez is elég:

(1..100).map{rand(100)}

Mert nem erted elegge a ruby nyelvet. A map a kifejezes ertekere mapolja at a tomb (illetve esetunkben range) elemeit. Az elso verzio csak es kizarolag azert mukodik, mert az ertekadas operatornak van visszateresi erteke, megpedig a jobb oldalon allo kifejezes. Egyeb esetben hibas lenne.

Egyebkent a legerthetobb mod talan ez:

rands = []
100.times { rands << rand(100) }

Bar ket sor, megis sokkal egyertelmubben leirja, mit szeretnel.

--
Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal

?

Szerk.: persze, nem kell "x =", mivel a kifejezés önmaga a visszatérési érték. lehetne:

.map{|x| x**2}

Számomra az volt új, hogy a "|var|" is elhagyható.

Ez az interpreter "hanyagsaga" (egyebkent egyszeruen nem vettem eszre, hogy elhagytad, elnezest, ritkan latok ilyen kodot). Elvben nem lenne elhagyhato, es nem is ajanljak, mert ha hirtelen elkezdik az atadott blokk aritasat ellenorizni (#arity) akkor csunya meglepetesekben lesz reszed.
Nalad ez azert hagyhato el biztonsagosan, mert nem hasznalod fel az x erteket. Egyebkent nem hagyhato el, es nem is ajanlott elhagyni, akkor sem, ha amugy nem hasznalod fel az atadott valtozo erteket.
--
Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal

"Ez az interpreter "hanyagsaga" ... Elvben nem lenne elhagyhato, es nem is ajanljak"

Ugye nem gondolod hogy a véletlen műve? Még jó hogy használni fogom! Hozzájárul az áttekinthetőséghez.

"..azert hagyhato el biztonsagosan, mert nem hasznalod fel az x erteket..."

Még jó hogy.

Felolem elhagyhatod, azonban nagyon figyelj oda, hogy mikor hagyod el. Van olyan, blokkokat hasznalo metodus, ami figyeli az atadott blokk aritasat, es totalisan mas kodot futtat le:

def foo(arg1, &block)
  if block.arity < 1
    make_world_peace
  else
    destroy_earth
  end

  yield(arg1)
end

Ezert nem mindegy. Es en ezert szoktam kiirni. Inkabb legyen egy picit attekinthetetlenebb a kodom, minthogy valaki suttyomban elkezdje ellenorizni az aritast, aztan debugolhatom, hogy mi a rak tortenik egy rejtelyes hibanal.
--
Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal

Nem értem, miért akarod azt megmagyarázni, hogy ha valaki rosszul olvas egy kódot, akkor az mindenképpen a dev rossz kódolási szokása miatt lesz? Minden nyelven lehet könnyebben olvasható kódit írni és fordítva. Ez egyértelmű, Ruby ennél még tömörebb megoldásokat is kínál. Viszont a fentinél nem rontja szerintem az olvashatóságot.

Ha meg változós kódot szúrsz be a változó definiálása nélkül, arra az interpreter hibát dob.

A fenti példa számomra nem életszerű.

Fejbol meg nem mondom pontosan, de asszem a Builder gemnel volt egy olyan, hogy ugy detektalta, hogy Ruby 1.8 vagy 1.9 alatt van, hogy felulvagott egy stdlib-es metodust, es figyelte, hogy milyen aritasu blokkal hivtak meg, mert a stdlib ebben kovetkezetes. Aztan ahol ezt a metudost megegszer felulvagtak, ott rejtelyes hibak (rejtelyes megdoglesek) kezdtek elojonni 1.8 alatt.

Arra akartam csak felhivni a figyelmet, hogy az interpreter csak azt nezi, hogy kell vagy nem kell valtozo, azt, hogy hany darab valtozo van, mar nem nezi. Ja, es a valtozot is a yield sornal ellenorzi - addigra mar regen nincs Fold.

De mondok mast:

def foo(eid = nil, &block)
  if block.arity < 1
    yield
  else
    yield Employee.find(eid)
  end
end

Es mar az optimalizalas teruletere tevedtunk, nem keressuk elo az employee-t, ha az atadott kodot ugysem erdekli.
--
Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal

omg. es meg komolyan is gondolja...

Majdnem most latok eloszor Rubyt, de a "1-tol 100-ig az elemek veletlen sorrendben" es a "100 db veletlen szam 1 es 100 kozott" az szerintem baromira nem ugyanaz.

A problema, hogy az elso allitasod sem all meg. Amit te mondasz, az ugyanis ez lenne:

(1..100).sort_by { |x| rand(100) }

Viszont itt a (1..100) range tenyleg csak az iteracio miatt van felhasznalva. Gyakorlatilag az a map biztonsagosan kicserelheto egy each-ra is, semmit nem valtoztatna.

Peldaul a

(2..101).map { rand(100) }

kod altal visszaadott tombben lesz egyes es nem lesz benne a 101.

Az Enumerable#map ugyanis azt csinalja, hogy _kicsereli_ az eppen iteralt enumerable - esetunkben tomb - elemeit a kifejezes ertekere.

Ezt peldaul olyasmire lehet felhasznalni, hogyaszondja:

Employee.find_all.map { |e| u.salary }

Es megkapjuk a dolgozok fizetesenek tombjet.
--
Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal