Een huwelijksaanzoek via Ruby
Een uit de hand gelopen grap waarbij ik iemand ten huwelijk heb gevraagd resulteerde dit keer in het schrijven van een Ruby-script. Het hele verhaal is iets te veel om hier te plaatsen, maar de korte versie is dat mijn eerste aanzoek mislukte en ik daarom een tweede zou proberen. Omdat deze persoon ook wel iets met code heeft (niet met Ruby helaas) besloot ik dit in Ruby te schrijven. Huwelijksaanzoeken gaan ten slotte om liefde en dat ik van Ruby houd zal geen geheim zijn…
De vorm van het hart komt van een soortgelijk Perl-script, maar de code is geheel zelf geschreven.
Dit zou natuurlijk nooit gebruikt mogen worden in een “productie”-omgeving, maar voor een grap is het wel eens leuk om je code wat vorm te geven.
De code is op deze blog niet helemaal mooi zo op de weblog, misschien kan ik hier later nog wat aan doen.
Hoe dan ok, laten we maar beginnen:
#!/usr/bin/env ruby -W0
@y = ""
you = "Susan"
love = you
def i (a,b)
puts @y end;str =
"Rtrzm, " + "vhk id zkridakh"
str.concat("des lds ld sqntvd")
str += "m"; str.split(//).each{
|bfiwdl| @y << bfiwdl.succ };
@y.split("").each{|qjwciqb|
@you = @y.gsub(/\!/," ")}
@you.gsub!("aa","a");id
@y = @you.gsub("-",
",");@y += "?";
@y.gsub!(#q
/\s+/,#
" ");
you
p
i love, you
Voor we aan de uitleg beginnen zal ik de code eerst wat opsplitsen in een leesbare structuur.
#!/usr/bin/env ruby -W0
@y = ""
you = "Susan"
love = you
def i (a,b)
puts @y
end
str = "Rtrzm, " + "vhk id zkridakh"
str.concat("des lds ld sqntvd")
str += "m"
str.split(//).each{ |bfiwdl|
@y << bfiwdl.succ
}
@y.split("").each{ |qjwciqb|
@you = @y.gsub(/\!/," ")
}
@you.gsub!("aa","a")
id
@y = @you.gsub("-", ",")
@y += "?"
@y.gsub!(#q
/\s+/,#
" ")
you
p
i love, you
Nu zal het ook duidelijk zijn dat er heel wat “overbodige” code in zit, maar die was soms nodig om de vorm succesvol te krijgen.
Regel voor regel er maar weer even door dan:
De eerste regel vertelt Ruby geen warnings te tonen door het gebruik van -W0. Dit voorkomt een warning over de methode id die eigenlijk niet meer gebruikt zou moeten worden. Op die plek had ik echter gewoon eventjes twee tekens nodig…
Daarna maken we de variable @y die in de hele class beschikbaar is, deze gebruiken we uiteindelijk om de output in op te slaan. De naam Susan wijzen we toe aan de variable you en you weer aan love.. We kunnen elke waarde in deze variablen stoppen, het gaat er om dat we ze nodig hebben aan het einde.
Dan begint het hart van de code, wat geopend wordt met een simpele methode die we i noemen en 2 parameters accepteer: a en b. Eigenlijk doen we niets met deze parameters, maar op deze manier kunnen we het later aanroepen als i love, you waarbij i gewoon een call is naar de methode en love en you de parameters zijn.
Alles wat we binnen deze methode doen is het printen van de waarde van @y.
De volgende stap is het aanmaken van de string die we willen printen. Normaal gesproken hadden we hier iets kunnen doen als @y = "Susan, wil je alsjeblieft met me trouwen?", maar het moest natuurlijk wat cryptisch zijn en het hart opvullen. De string maken we dan ook in een aantal stappen.
Eerst maken we simpelweg het eerste deel van de string aan, gebruiken we concat() om er nog een stukje aan toe te voegen en ten slotte += om de laatste letter toe te voegen.
Vervolgens doen we een split op de string, met als argument een lege regular expression. Het resultaat hiervan is dat we een array hebben waarin alle letters van de string afzonderlijk zijn opgeslagen. Met een each-block lopen we door de array heen en hebben we in de variable bfiwdl steeds de volgende letter van de string zitten.
Bij de eerste iteratie is dat dus een R, vervolgens een t etc. De naam bfiwdl is gekozen omdat dat het aantal karakters was wat ik nodig had om die regel op te vullen. Hier beginnen we ook de @y-string te vullen. @y << bfiwdl.succ is gewoon een andere schrijfwijze voor bijvoorbeeld @y += bfiwdl.succ. Zoals wel vaker kent Ruby gewoon verschillende methodes om iets uit te voeren.
De methode succ pakt steeds het volgende karakter, zoals dat voor een mens ook enigszins logisch zou zijn. Na een ‘a’ komt dus een ‘b’ en na een ‘c’ een b. Als we nu terugkijken naar de string die in str zit (Rtrzm, vhk id zkridakh des lds ld sqntvdm) zal het misschien ook opvallen dat we dus steeds een letter “terug” in het alfabet zijn gegaan.
Door de functie succ pakken we steeds de volgende waardoor we iets leesbaars krijgen. Als dit gebeurt is splitten we op onze nieuwe string. Dit keer geven we een lege string mee als argument aan split, wat hetzelfde resultaat geeft als een lege regular expression. Wederom een voorbeeld hoe je een probleem soms op meerdere manieren kan oplossen: iets wat erg handig kan zijn als je een stukje code schrijft wat enigszins cryptisch moet zijn.
De naam qjwciqb is weer puur gekozen vanwege lengte, deze variable gebruiken we verder ook niet in het blok. Wel gaan we de nieuwe variable @youineens gebruiken. Hier komt een string in te zitten die lijkt op de string in @y, maar een verbeterpunt heeft. Door het gebruik van .succ zijn de spaties in de string vervangen door uitroeptekens. Met gsub (een methode om tekst in een string te vervangen), zetten we de uitroeptekens terug om naar spaties.
Ook de dubbele a gaan we vervangen. Ruby vindt dat de ‘z’ opgevolgd moet worden door ‘aa’, iets waar ik het niet mee eens ben. Met @you.gsub!("aa","a") zet ik dit weer “goed”. Iets verder doen we een dergelijke replace als de vorige twee om van een - terug een komma te maken.
Voor die regel zien we echter nog een (nutteloze) call naar de functie id. Zoals gezegd bij de bespreking van regel 1 is deze niet nodig en puur voor opvulling van het hart.
We zijn weer terug aan het werk in @y en voegen nu het vraagteken toe aan het eind van de string. Het is ten slotte een verzoek, geen opdracht. Of dat is op z’n minst wat ik wil dat zij denkt.
Om het af te maken vervang ik dubbele spaties met een regular expression nog door een enkele spatie. Dit werkt als volgt: \s is het teken voor een spatie in regular expressions. \s+ Wil zeggen: één of meer spaties achter elkaar. Door deze te vervangen door een enkele spatie werd een probleem wat ik eerder had opgelost. Bij een latere iteratie van de code was de string eigenlijk al gelijk goed, maar toch heb ik het laten staan.
De # staan er weer voor … opvulling.
De laatste twee regels: you en p doen eigenlijk niets. Tegen het einde van zo’n hart wordt het erg smal en heb je gewoon wat korte namen nodig…
De i love, you op de laatste regel heb ik al uitgelegd in het begin: dit roept gewoon de functie i aan met parameters love en you.
Uiteraard is dit geen code die ooit in productie zo geschreven zou mogen worden, maar toch zitten er dingen in die ook in productie gebeuren:
- Code die niet meer gebruikt wordt laten staan in plaats van verwijderen
- De waarde van een variable in een andere stoppen en dan weer terug. Gelukkig zie ik dit weinig, maar ik heb het al meegemaakt…
- Functies die eigenlijk geen nut hebben in de code zetten
- Onduidelijke variabelenamen (zoals
bfiwdl) - En natuurlijk: 15 regels code gebruiken voor iets wat ook in één regel opgelost had kunnen worden.
Kleine note nog: ik heb nog geen ja of nee gehoord van het bewuste meisje. Iemand tips om er een ‘ja’ uit te krijgen?

15-10-2007 13:18
OMFG
15-10-2007 16:19
Steven, jij bent ervaren.
Hoe kreeg jij je vrouw zo ver dat ze ja zei?
16-10-2007 16:19
Tja, als je zo knap bent als ik is dat niet zo moeilijk hoor. :-p
16-10-2007 16:32
Dan zou het voor mij toch ook geen probleem moeten zijn?
16-10-2007 17:47
Veel bier. Echt veel bier.
16-10-2007 23:34
Mooi gedaan joh, en dat antwoord komt vanzelf wel..
19-10-2007 09:47
Martijn, ik weet niet of je je vergist hebt, maar ik zei wel “knap” he :-p
30-10-2007 19:58
HOWTO: Bash terminal in Mac OS X afstellen…
Toen ik gisteren echo $PATH typte om te zien hoe m’n path er uit zag schrok ik toch wel een beetje van de vele regels die over m’n scherm heen scrollde. Ik heb ze niet geteld, maar het waren er veel meer dan ik nodig acht.
Voor de mensen d…