Praktika ja õppida

Link: http://crypto.stanford.edu/~blynn/haskell/codejam.html

Sooviksin teha paremini programmeerimine võistlused, aga mul on vähe aega, et treenida. Veelgi enam, ma olen enamasti huvitatud algoritme; Ma olen palju vähem huvitatud viimistlusjärgus stereotüüp, mälu eraldamise, vältides ülevoolu ja nii edasi.

Seega Haskell: ta hoolitseb palju paberimajandust, tänu funktsioone, nagu tüübist järeldada, Prügikoristus ja suvalise täpsusega täisarv aritmeetika.

Keskendume Google Code Jam on “praktika ja õppida” lehele, tänu järgmised omadused:

Hetkel kohtunik.

Endale mingit keelt.

Diverse kvaliteetne probleeme.

Lahendused, nii et ma ei ole kõhkluseta avaldades oma.

Reverse Sõnad

Meie ülesanne on lihtne väljendada inglise keeles: eraldusribadega ruumid, liituda sõnu pöördel sõnade nimekiri antud sirge.

Meie ülesanne on väidetavalt isegi lihtsam väljendada Haskell:

> Unwords (reverse (sõnad ( “saab puuri neelama ei saa sa”)))
“Sa ei saa neelata puuri saab”

Nagu võiks arvata, sõnad lõheneb eraldusjoone sõnu, tagurpidi pöörab nimekirja ja unwords liitub sõnu üheks kooskõlas eraldab ruumid.

Meil muuta see täielikult programmi rahuldust võistluse tingimused InterAct, funktsioon, mis läbib kogu sisendit hiiglane stringi funktsioon meie valik, siis prindib tagasi string standard väljund.

– Reverse-words.hs
Peamised = suhelda $ unlines. zipWith (++)
[ “Case #” ++ Näita t ++ “:” | t

lahendada = unwords. pöörata. sõnad

($) Operaator säästab meile sulgudes. Üldiselt saame kirjutada midagi (b (c (d))), kui vähem täis $ b $ c $ d.

Kood pärast $ määratleb funktsiooni võtame suhelda. Kuna Liitfunktsioon operaator (.) On õige-assotsiatiivne, et mõista meie definitsiooni, loeme seda paremalt vasakule.

See murrab sisend string read (read), siis ignoreerib esimene rida (saba); esimene rida sisaldab mitmeid test juhtudel, mis on mugav keelte primitiivne mälu eraldamise rajatised.

Siis kulgema meie algoritm iga sisendrida (kaart lahendada) ja ülaloleva “Case # -kordne:” tulemuse (zipWith (++) ja loetelu mõistmine). Lõpuks liituda kõik väljundliinidele koos hiiglane string (unlines) tagastama suhelda.

See väike programm esitleb mitu kroon pärleid Haskell:

point-free programmeerimine: selle asemel, et määratleda “f (x) = g (h (x)) iga x”, me loobuda häirivad muutuja x ja lihtsalt öelda, f = g. h.

nimekirja arusaamade: väljendid nagu [t ^ 2 | t

laisk hindamine: miks lõpmatu nimekirja [1 ..] toimib? Kuna Haskell ainult arvutab nii palju kui ta vajab. Lazy atesteerimist tähendab ka meie programm ei oota kogu sisendi enne kolimist järgmine samm. Vastupidi, see on rohkem nagu torustiku kest: vaid loeb ja arvutab kohta nii palju sisend on vaja printida järgmine sümbol.

kaart: pool Google’i kuulus MapReduce on oma nime saanud selle funktsiooni. (Teine pool on oma nime saanud teise funktsionaalse programmeerimise pärl, mis Haskell programmeerijad helistada korda asemel “vähendada”.)

currying: me komponeerimise kaart lahendada teiste funktsioonide (.), kuid kuidas on kaart lahendada ülesanne? Lihtne: see on funktsioon, mis kaardistab lahendada funktsiooni üle antud sisendi nimekirja. Teisisõnu, kui f = kaardilt lahendada, siis f x = kaardilt lahendada x.

paindlik fikseeritus: saame tantsida vahel Siseliidete ja eesliite märke, segades kaks maksimaalse selguse.

trafaretset-free tugevad staatilised liiki: meil ei ole tüüpide deklaratsioonides, kuid mitte ainult see on võimatu segadusse stringi int, aga see on ka võimatu segadusse puhas funktsiooni ebapuhtad funktsiooni. Ainus ebapuhas funktsioon on siin toimida; Ülejäänud programm on puhas.

võrdsed õigused: paljudes keeltes, võrdusmärk tähendab loovutamine, mitte võrdsust. See tähendab, et asjad paremal küljel saab määrata midagi vasakul pool viitab. Haskell, eeldusel, et funktsioon on puhas, igal ajal näeme asju ühel pool võrdusmärki, saame vahetada see välja asju, teiselt poolt vara tuntud viidatavus.

Aga piisavalt naba-vahtis nüüd. Pärast allalaadimist sisendandmed (via veebilehel), oleme kompileerida ja käivitada programm, siis esitab väljund (via veebilehel) mingi lihtne punktid:

$ GHC reverse-sõnad
$ ./reverse-words välja

Me võiks kirjutada kiiremini lahenduse C: me antud piire sisendi suurusest, nii et me võiks teha kõike virna ning vältida kuhjuma manipulatsioonid, Haskell tuleb teed kulisside taga. Minu noorem ise oleks seda teinud, ja meeldis kiirus.

Kui palju sõiduaega oleks seda maha ajama? Olgem suuremeelne ja öelda, et meil võib päästa 5 sekundit abil C. Nüüd mõtle kui palju aega kulub, et kirjutada täielikku C programmi, võrreldes 3 sõnad ja 2 punkte Haskell. Isegi arenenud IDE, siis on raske ette kujutada kokkuhoidu sõiduaega hüvitada kulud, programmeerimine aega.
Hoida Credit

Kuna me võime eeldada, iga sisendi test on unikaalne paar esemeid, mille maksumus summast soovitud kogus, saame kirjutada lihtne lahendus nimekirja mõistmine:

lahendada c ps = let m = pikkus ps – 1
pea [täpsem (i + 1) ++ “” ++ näidata (j + 1) |
i

Siin on c soovitud summa ja ps on nimekiri hinnad objekte. Me laksu DAB koodi sõeluda sisend ja trükkida “Case # -kordne:” preambulis:

Peamised = suhelda $ unlines. zipWith (++)
[ “Case #” ++ Näita t ++ “:” | t

f [] = []
f (_C: _: _ ps: s) = lahendada (loe _C) (kaart lugeda $ sõnad _ps): f s

Kuid see lahendus on aeglane. Haskell loetleb tõeliselt on seotud nimekirju, nii soojaks nda liikme võtab n samme, mis tähendab kuupmeetri sõiduaega. Me peaksime selle asemel kasutada massiivid risttahukakujuline-time lahendus. (A hash tabelit võiks lahendada probleemi lineaarne aeg, kuid sisendid on nii väike, siis võib samuti jääda massiivid.)

import Data.Array

Peamised = suhelda $ unlines. zipWith (++)
[ “Case #” ++ Näita t ++ “:” | t

f [] = []
f (_C: _: _ ps: s) = lahendada (loe _C) (kaart lugeda $ sõnad _ps): f s

lahendada c ps = lasta
n = pikkus ps
a = listArray (1, n) ps
pea [näitama i ++ “” ++ näidata j |
i

Nimekirjad ja massiivid

Kaks lahendusi eespool olla sissejuhatus nimekirjad ja massiivid Haskell.

Haskell nimekirju on rõõm töötada. Seisab õlgadele Lisp, palju mõelnud läks oma disaini ja aastate jooksul, rikkalik ja luksuslik raamatukogu nimekirja funktsioonid on arenenud ja küpsenud.

Massiivid teiselt poolt, kuigi talutavaid, on vähem rafineeritud. Jooksul Data.Array nimeruumi on mitu liiki massiivid valida, kõik aeglasem kui vana hea C massiivid. Lisaks Data.Vector võib olla parem mõnikord, ja see moodul tuleb paigaldada eraldi baasi süsteemi.

Uuendamine ühe massiivi element võtab konstantse ajaga teistes keeltes, kuid kui me tegema erilisi pingutusi, Haskell, see toiming on sama kallis kui kopeerimine kogu massiivi.

Seega väiksemate probleemidega, mida me sageli kättesaamatu nimekirjad esimene ja kaaluda massiivid hiljem, vastupidine meie käitumist, kui programmeerimine C-like keeles.

Comments are closed.