Inlägg

Inlägg som wgren har skrivit i forumet
Av wgren

Dubbelt så mycket vatten som havre. Koriander, chilipulver och svarta jästa sojabönor, en tärnad tomat om jag har. Stek ett ägg samtidigt som gröten kokar, skiva och rör i. 💪
Har försökt knäcka ägget i direkt men då blir gröten för tjock och bränner alltid fast i kastrullen.

Om jag känner för sött så äpple och kanel iatället.

Av wgren

Hmm förutom spel så håller jag på med machine learning kurser, så jag skulle inte ha något emot ett 3080 kort med 20 GB minne. Får CUDA Out of Memory error på massa exempel. Men kort med mycket och snabbt minne verkar ju vara dom mest attraktiva mining korten som ni säger, så man får väl köra mindre batch sizes och rulla tummarna lite mer. Eller bita ihop och betala för ett 3090 kort.

Eventuell mining-reducerande åtgärder borde väl inte påverka ML väl? Som jag förstått det är det mest hashning som stryps, och ML är ju nästan enbart (matris) multiplikationer?

Av wgren

Dag 16, Scala. Jag tar det lite lugnare nu och koncentrerar mig på att lära mig saker och få till bra lösningar. Den här blev ok tyckte jag, nöjd med "uniques" lösningen.

object Day16 { type Ticket = List[Int] case class Field(name: String, ranges: Array[Range.Inclusive]) { def matches(x: Int): Boolean = ranges.exists(_.contains(x)) def matches(l: Seq[Int]): Boolean = l.forall(matches) } def toField(in: String) = { val Array(name, rs) = in.split(": ") val ranges = rs.split(" or ").map(s => s.split("-") match { case Array(h, t) => Range.inclusive(h.toInt, t.toInt) }) Field(name, ranges) } class Categorizer(in: String) { val Array(fds, my, ticks) = in.split("\\n\\n").map(_.split("\\n")) val tickets: List[Ticket] = ticks.drop(1).map(_.split(",").map(_.toInt).toList).toList val myTicket = my.last.split(",").map(_.toInt).toList val fields = fds.map(toField).toList def included(in: Int, ranges: Seq[Range.Inclusive]) = { !ranges.forall(!_.contains(in)) } def validTickets = { val ranges = fields.flatMap(_.ranges) tickets.filter(_.forall(i => included(i, ranges))) } def possibleCategories: List[List[Field]] = validTickets.transpose.map(column => fields.filter(_.matches(column))) def uniques[A](in: List[List[A]]): List[A] = { for { l <- in l2 <- in if l.size - l2.size == 1 } yield l.diff(l2).head } def uppg1: Int = tickets.flatten.filter(!included(_, fields.flatMap(_.ranges))).sum def uppg2: BigInt = { val departureIndexes = uniques(possibleCategories).zipWithIndex.filter(_._1.name.startsWith("departure")).map(_._2) departureIndexes.map(myTicket(_)).foldLeft(BigInt(1))(_ * _) } }

Dold text
Av wgren
Skrivet av jaqob:

Tack för länken. Har tjuvkikat på en del andra lösningar idag, och sett att de flesta använder någon variant av denna. Att implementera den är trivialt, men jag förstår ärligt talat fortfarande inte intuitivt hur och varför det fungerar....

Dold text

Om du går längst en stig och vet att det finns två olika vägar att hitta till punkten du är nu. Du ser tre andra vägar framåt, du går till den första, där har någon skrivit "det finns 5 olika sätt att hitta hit" på en lapp, du stryker 5 och skriver 7 istället, och gör så på alla tre vägarna. Finns det ingen lapp på någon av dom resterande så får du skriva en ny med "finns 2 olika sätt" på och lägga dit. När du gått genom alla vägar så kollar du lappen i Rom (eftersom alla vägar leder till Rom) för att se alla möjliga vägar dit.

Dold text
Av wgren
Skrivet av Larrxi:

hur hade man då löst det?

Kollegas förklaring på hans eleganta lösning:

Av wgren

Jahapp, uppgift 1 löste jag på under 10 minuter och med en oneliner så kände mig väldigt mallig, men har kört fast helt på uppgift 2, även efter att jag gav upp och tittade på andras lösningar. Hilfe?

Edit: Never mind, hittade buggen.

Ger rätt på exempelinput (exvis 19208), och är snabb. Men ger för högt värde på riktiga input enligt AoC. But why?

def paths(in: List[Int]) = { in.tails.filter(!_.isEmpty).foldLeft(Map[Int, BigInt](0 -> BigInt(1))) { case (map, head::tail) => { val validAdapters: List[Int] = tail.takeWhile((x) => x - head < 4) val currentPaths: BigInt = map.get(head).get validAdapters.foldLeft(map) { (nmap, elem) => nmap.updated(elem, currentPaths + nmap.get(elem).getOrElse(BigInt(0)) ) } } }.get(in.last) } // Uppgift 1 input.foldLeft((0,0,0)) { case ((last, ones, threes), x) => if (x-last == 3) (x, ones, threes+1) else if (x-last == 1) (x, ones + 1, threes) else (x, ones, threes) }

Felet: Jag hade gjort en import scala.collection.mutable._ i min REPL när jag satt och labbade, så någonstans refererade jag till en mutable Map, eller det blev en implicit conversion eller något. Startade jag om min REPL och pastade koden ovan funkade det.

Dold text
Av wgren
Skrivet av jclr:

Jag hade helt glömt bort AoC i år. Gick tillbaka och löste några av de kortare problemen också.

Kul med någon annan som kör Scala också! Se där, dag 9 uppgift 1 gick att lösa funktionellt också, misstänkte det men gav upp.

Av wgren

Inte lika snabb som Go, inte lika snygg som Python, men jag löste det i alla fall. Dag 9, Scala.

object Xmas extends App { def apply(l: List[BigInt]) = new Xmas(l) //Uppgift 1 def findNonValid(in: List[BigInt], preambleSize: Int):Option[BigInt] = { val candidates = in.drop(preambleSize) candidates.foldLeft(Xmas(in.take(preambleSize))) { (acc, elem) => acc.add(elem) }.nonvalids.headOption } //Uppgift 2 def find(list: List[BigInt], wanted: BigInt): Option[BigInt] = { for { x <- 0 to list.size y <- x to list.size if (y - x > 1) slice = list.slice(x,y) if (slice.sum == wanted) } return Some(slice.min + slice.max) None } class Xmas(val preamble: List[BigInt]) { import scala.collection.mutable val nonvalids = new mutable.Queue[BigInt]() val source = new mutable.Queue().addAll(preamble) def valids = (for { x <- source; y <- source; if x!=y } yield (x + y)).distinct def add(x: BigInt): Xmas = { if (source.contains(x) || !valids.contains(x)) nonvalids.addOne(x) else source.addOne(x).dequeue() this } } }

Uppgift 2 försökte jag först skapa alla permutations rekursivt med immutables och sen köra fold, out of memory inte så oväntat. Sen försökte jag en rekursiv med head/tail pekare, avbröt den efter 5 minuter (kan ha funnits en bugg). Denna lösning kör på lite under en sekund vilket ändå är rätt kasst jämfört med Flexberts 16μs, ska fundera hur jag kan optimera både snygghet och hastighet. Efter att jag hunnit igenom nästa ML kapitel i boken.

Edit: Kunde ju inte släppa det här. Nytt försök på tvåan:

def find(list: List[BigInt], wanted: BigInt): Option[BigInt] = { for { x <- 1 to list.size slice <- list.sliding(x) if (slice.sum == wanted) } return Some(slice.min + slice.max) None }

Nu körde den på 8 mllisecs. Ett par orders of magnitude snabbare än tidigare. Forfarande 3 orders of magnitude långsammare än Go, men det får duga för icke-kompilerad, icke-jittad kod som körs i en REPL.

Dold text
Av wgren

Dag 8 Scala. Uuugh. Satt över 4 timmar idag. Tänkte "bra, nu kan jag ta en promenad och få lite sol kanske för att rensa huvudet och bli lite gladare". Tittar ut, det är "molnig vinterdag under pandemiåret 2020 regisserad av Ingmar Bergman".

object Emulator extends App { sealed trait Errors case class InfiniteRecursion(line: Int) extends Errors case class OobAttempt(line: Int) extends Errors sealed trait Ops case class Nop(steps: Int) extends Ops case class Jmp(steps: Int) extends Ops case class Acc(value: Int) extends Ops def apply(program: List[String]) = new Emulator(program) def decode(str: String):Ops = (str.take(3), str.drop(4).toInt) match { case ("nop", amount) => Nop(amount) case ("acc", amount) => Acc(amount) case ("jmp", step) => Jmp(step) case _ => throw new IllegalArgumentException(s"Unknown op $str") } class Emulator(val program: List[String]) { def step(seen: Set[Int], acc:Int, pos:Int, fixed: Boolean = false):Either[Errors, Int] = { if (seen.contains(pos)) return Left(InfiniteRecursion(pos)) if (pos < 0 || pos > program.size-1) return Left(OobAttempt(pos)) val op = decode(program(pos)) val newValue = op match { case Acc(value) => acc + value case _=> acc } //Horray, the program terminated and we have an answer if (pos==program.size-1) return Right(newValue) val newPos = op match { case Jmp(steps) => pos + steps case _ => pos + 1 } val newSeen = seen + pos def result = step(newSeen, newValue, newPos, fixed) (fixed, result, op) match { case (_, Right(answer), _) => result //we have an answer, return it case (true, Left(err), _) => result //Already tried fixing once, not allowed more attempts. case (false, Left(err), Nop(x)) => step(newSeen, acc, pos + x, true) //Change to Jmp case (false, Left(err), Jmp(_)) => step(newSeen, acc, pos+1, true) //Change to Nop case (false, Left(err), _) => result //Accs are never wrong, nothing to do here. } } def run(): Either[Errors, Int] = { step(Set[Int](), 0, 0) } } }

Blev väl acceptabel tillslut, men "step" är för lång, den gör return på 8 olika ställen, och är svår att testa på något utom full programinput. Känns inte som det blev så funktionellt denna gång.

Dold text
Av wgren
Skrivet av BigMomma:

Dag: 7
Språk: VB.Net

Som Ruby programmerare (bland annat) så måste jag säga att VB.Net är behagligt läsbart. När man börjat vänja sig vid språk som inte har brackets i olika former och semikolon överallt så sticker dom i ögonen när man kommer tillbaka till språk (C och alla dess barn exvis) som kräver dom.

Av wgren
Skrivet av Dave1080:

Dag 7. Läs inte om du vill lösa uppgiften helt på egen hand. Jag behöver hjälp.

> Är det meningen att man ska loopa mer än två gånger? I så fall hur vet man när man kan sluta loopa?

Jag löste båda rekursivt (metoder som anropar sig själva med nya parametrar). Har en kollega som löste det på ett annat sätt för han gillar inte rekursion (tycker det är krångligt) så det går uppenbarligen, typ med en while-loop och muterbara datastrukturer kan jag tänka. Men så här löste jag dom (i pseudokod).

Uppgift 1: Parsa filen så du får en Map(String -> List[String]). Vi struntar i siffrorna i uppgift 1, så det blir exvis

"blå" -> ["vit", "röd", "guld"] "svart" -> ["blå"] "vit" -> [] Skapa en metod "sökaren" som tar emot två Stränglistor: attUndersöka och hittade. define sökaren(attUndersöka, hittade): IF attUndersöka listan är tom RETURN "hittade" listan parametern precis som vi fick in den. Nu är vi klara, kallas basfallet i rekursion) ELSE plocka ut ett element "x" från attUndersöka så attUndersöka är ett element mindre. lägg "x" i listan över hittade gå igenom datastrukturen och hitta dom alla dom rader där listorna innehåller "x". lägg till nycklarna som pekar på dom listorna till listan attUndersöka OM dom inte redan finns där RETURN sökaren(nyaAttUndersöka, nyaHittade)

Så anropar du sökaren med Listan ["guld"] och tom lista, eftersom det är guld vi vill undersöka, och vi har inte hittat något än.
Första varvet: attUndersöka är inte tom. Så vi plockar ur "guld", och hittar att blåa väskor innehåller guldväskor. Vi anropar sökaren med Listorna ["blå"] respektive ["guld"] och returnerar vad det varvet returnerar
Andra varvet: attUndersöka är inte tom. Ta ut "blå", hitta "svart". Anropa sökaren med ["svart"] respektive ["guld", "blå"] och returnerar du vet vad.
Tredje varvet: attUndersöka är inte tom. Ta ut svart, hitta inget. Anropa sökaren med tom lista och ["guld", "blå", "svart"]
Fjärde varvet: attUndersöka är tom, vi returnerar hittade: ["guld", "blå", "svart"] och det poppar upp och blir slutgiltiga svaret. Längden på "hittade" minus ett (för vi skiter i "guld", vi ville bara ha hur många möjliga yttre väskor den har) är svaret.

Uppgift 2:

Kolla på min kod ovan och se om du förstår, jag orkar inte skriva en så lång förklaring igen.
#18791461
Men allt fram till totalWeight läser och parsar bara filen till en Map så som jag vill ha den, den här gången måste vi bry oss om siffrorna i texten också. Så själva körningen är totalWeight som anropar sig själv tills den är klar efter att jag anropat den första gången. Men istället för en "hittade" lista så returnerar den rekursivt vad varje väska innehåller (gånger antal av den typen).

GLaDER hade en rätt tydlig förklaring på uppgift 2 också tyckte jag.

Dold text
Av wgren

Inte nöjd med dagens insats eller lösning. Först stötte jag på en bugg, när jag körde ScalaTest med "sbt test" så kastade allt användande av regex matchers en Java nullpointer exception oavsätt input. Men körde jag det som ett standalone program eller via Ammonite REPL funkade identisk kod klockrent. Förmodligen någon versionskonflikt av senaste versionerna av OpenJDK Java + Scala + sbt + ScalaTest som orsakar. Orkar jag gräva i det och submitta en buggrapport eller en eventuell open source fix...?

Sen slösade jag jättemycket tid på

att försöka bygga upp en weighted graph med fold till en custom Tree klass, ville triumfera med snygg oneliner igen. Men blev soppa av allt, särskilt som jag inte kunde skriva unit tester. Och tiden drog iväg så slutade med att jag gjorde rekursiv lookup på map. Känns hackigt, men tröstar mig med att Ingetledigtnamn är inne på liknande spår. Och att när jag tänker efter så är det ju en utmärkt weighted graph struktur som jag gjorde med min Map, så behöver inte skriva någon egen klass. Jag behövde bara komma på hur man traverserade den rätt.

val BAG_MATCHER = " ?(\\d+) (.*) bag.*".r case class Bag(weight: Int, color: String) def contentToList(s: String): List[Bag] = { s.split(",").toList.flatMap { case BAG_MATCHER(number, color) => List(Bag(number.toInt, color)) case default => List() // "no other bags" och ev andra strängar = tom väska } } def lineToTuple(s: String): Tuple2[String, List[Bag]] = { val color::content = s.split("bags contain ").map(_.trim).toList (color, contentToList(content.last)) } val bags = (read.lines! pwd/"input.txt").map(lineToTuple).toMap def totalWeight(color: String): Int = { bags(color) match { case List() => 1 case list => (list.map { bag => bag.weight * totalWeight(bag.color) }.sum) + 1 } } totalWeight("shiny gold") - 1

Dold text
Av wgren
Skrivet av gibbon_:

Ugh, nu börjar det kännas som att awk har gjort sitt för i år.

Ändå fascinerad hur mycket du lyckats lösa på det spåret, och imponerad av hur snyggt det ofta blev. Snyggt jobbat!

Av wgren
Skrivet av Ingetledigtnamn:

Tror nästan jag måste byta språk. Att döma av lösningarna jag sett i dag är det ganska många som kör set-baserade lösningar och då blir det ganska enkelt, men detta slår rekord.

Kul att frälsa folk till mitt favoritspråk, men du har ju map, filter, reduce och dom andra på collections i Python också, så tror nog att du kan få till något liknande.

Av wgren
Skrivet av Ingetledigtnamn:

Dag 6, Python one-liner + Python, inte fullt så komplex och med kommentarer.

Just ja, unions och intersections är bra. En ännu mindre lösning för dag 6 uppgift 2 i Scala, inspirerad av din lösning:

val groups = (read! pwd/"input.txt").split("\\n\\n").map(_.split("\\s+") groups.map(_.reduce(_ intersect _).size).sum

Irriterad på att jag inte kommer på hur man får en referens till instansmetoder i Scala utan att ha en instans. I Kotlin eller Java 8 och framåt skulle det varit exvis "String::intersect" (Edit: som Dave1080 visar snyggt exempel på nedan.)

Lösningsförklaring: reduce tar en binär funktion som appliceras på alla element i en collection parvis tills du har ett element kvar istället för en collection. Så jag gjorde en lite kryptisk shorthand motsvarande typ

List("abc", "ab", "ac").reduce { (firstString, secondString) => firstString.intersect(secondString) } // Returnerar strängen "a" val intersectioncounts = intersectionStrings.map( (str) => str.size) val finalAnswer = intersectioncounts.sum // "sum" finns fördefinerad på numeric collections som collection.reduce { (x,y) => x + y }

Dold text
Av wgren

Dag 6 Scala:

import ammonite.ops._ val groups = (read! pwd/"input.txt").split("\\n\\n").map(_.split("\\s+") groups.map(_.flatten.distinct.size).sum //Uppgift 1 groups.map{ (group) => group.flatten.groupBy(identity).count { case(_k,v) => v.size == group.size }}.sum //Uppgift 2

Eller om jag skulle skriva lite mer maintainable kod som jag inte skulle vilja slå ihjäl mig själv om jag kom tillbaka till för att fixa en bugg i en vecka senare. Inklusive kommentarer för dom som inte kan Scala:

def count(group: Array[String]): Int = { //För input Array("ab", "ac") gör jag flatten så vi får Array('a','b','a','c') //groupBy omvandlar till Map och funktionen vi skickar in berättar hur man väljer nyckel. // "identity" är inbyggd, det är (x) => x funktionen. //Så nu får vi exvis: Map('a' -> Array('a', 'a'), 'b' -> Array('b'), 'c' -> Array('c')) val map:Map[Char,Array[Char]] = group.flatten.groupBy(identity) //Räkna hur många entries som alla svarat på, exvis Map('a' -> Array('a', 'a') map.count { case(_key, valueArray) => valueArray.size == group.size } } } //Mappa så vi omvandlar vår Array från att innehålla Arrays av String till att vara en Array of Int. Summera innehållet i Arrayen. groups.map(count).sum

Kom på att en sak som kan vara lite förvirrande är att jag använder lite olika sätt att referera till funktioner. I Scala kan man för exvis map skriva:

val l = List("hej", "hopp") //variabeln l är List[String] men det behöver jag inte skriva, det infereras. l.map( (s: String) => s.size) //Ger oss List(3, 4). Detta kan även skrivas som: l.map(_.size) //Underscore placeholder shortcut, Scala vet att det är en List[String] så kan använda String metoder på _ //Eller om vi har en funktionsreferens kan vi skicka in den direkt till map, Scala matchar in parametrarna och deras typer. //Identity finns i PreDef så den är alltid tillgänglig: https://www.scala-lang.org/api/current/scala/Predef$.html#ide... l.map(identity) //Returnerar List("hej", "hopp") inte oväntat. //Och om man vill pattern matcha på tupler - det här slet jag länge med innan jag hittade rätt lösning //första gången. Blir enkel kod men lite komplicerad typteori under. val tuples = List(("foo",1),("bar", 2)) tuples.map ( (a,b) => ..) //detta kompilerar inte, map metoden kräver alltid en funktion som tar en parameter, inte två. tuples.map { (a) => val stringPart = a._1 //Enda stället med one-based indexing i språket. Mycket irriterande. val intPart = a._2 } //Kompilerar, men känns pratigt. //Pattern matching to the rescue. Nu slipper vi se 1-indexing också. //Det innanför måsvingarna omvandlas till en PartialFunction[A,B] som alltså matchar parametern som behövs för map. tuples.map { case(stringPart, intPart) => ... }

Dold text
Av wgren
Skrivet av Texaco:

Hur noggrann tycker ni man ska vara? Tänker typ på första dagen om listan skulle varit ett element som var 1010 så borde detta felhanteras, men det krävs inte för att lösa uppgiften.

Det är ju inte produktionskod som måste framtidssäkras direkt, du har kompletta listan med input just nu.

Men som en heads-up så har jag hört från kollegor som kört det här tidigare att det mot slutet av kalendern ofta kommer uppgifter som bygger på varandra, exvis att man först får att man ska skriva en op decoder, och sen ska byggs det vidare på den efterföljande dagar tills man tillslut har en grundläggande CPU emulator. Så det kan vara värt att få igång vanan att skriva ordentliga unit-tester så man kan fånga eventuella edge cases.

En kollega fastnade ordentligt på dag x+5 och slet hela helgen med felsökning för han trodde han hade bugg i den dagens kod, och så var det en bugg i op-decodern som han skrev dag x, som inte detekterades med tävlingsinput från den dagen...

Edit: Men om det hade varit produktionskod så absolut, även för dag 1, man vet aldrig vad för skräp man får in även från klienter som säger sig följa en standard. Ibland är det till och med deras egen standard som dom inte följer! En bra tumregel för publika APIer är "be liberal in what you accept and conservative in what you send". Jag tolkar det som att programmera defensivt mot all input du inte kontrollerar, men kasta inte exception för minsta fel. Men däremot när du retunerar värden eller själv anropar andra, se till att följa standarder till punkt och pricka.

Av wgren
Skrivet av Ingetledigtnamn:

Dag 5, Python.
Hade varit lätt att göra en enradare av denna genom att om man gick igenom listan två gånger, men det tog emot. Så idag blev det två uttryck. För en van Python-programmerare skulle jag nog säga att det är lätt att förstå vad som görs.

seats = [int(line.translate({70:48,66:49,82:49,76:48}), 2) for line in open("input")] print(max(seats), *[i + 1 for i in range(128 * 8) if i in seats and i + 1 not in seats and i + 2 in seats])

Dold text

Kört lite Python tidigare men fick titta ett tag innan jag förstod.

Translate gör strängen till binary, int parsar med base 2 så du direkt får stolsnumret?

Dold text

Snygg lösning.

Nu känns min lösning tafflig i jämförelse, men det var kul att skriva lite rekursion.
Scala dag 5:

def reducer(s: String, range: List[Int], low: Char, high: Char): Int = { if (s.isEmpty) range.head else { val reducedRange = s.head match { case `high` => range.takeRight(range.size/2) case `low` => range.take(range.size/2) } reducer(s.tail, reducedRange, low, high) } } def row(s: String): Int = reducer(s.take(7), (0 to 127).toList, 'F', 'B') def columns(s: String): Int = reducer(s.drop(7), (0 to 7).toList, 'L', 'R') def seat(s: String) = row(s) * 8 + column(s) import ammonite.ops._ //Syntaktiskt socker för filläsning val seats = (read.lines! pwd/"input.txt").map(seat) // Uppgift 1 seats.max //Uppgift 2 val all = (seats.min+1 to seats.max-1).toList all.diff seats

Dold text

Edit: Jag var tvungen att se hur mycket jag kunde Pythonifera Scala koden.

import ammonite.ops._ //Syntaktiskt socker för filläsning def toSeat(s: String) = Integer.parseInt(s.map { case 'F' => '0'; case 'B' => '1'; case 'L' => '0'; case 'R' => '1' }, 2) val seats = (read.lines! pwd/"input.txt").map(toSeat) (seats.min+1 to seats.max-1).toList diff seats

Dold text
Av wgren

Älskar språk med stark pattern matching, Elixir, Kotlin och TypeScript kan göra liknande vet jag, TS till och med matcha på strukturella typer vilket är snyggt. Jag tror nyaste Java versionen har matchining liknande Scala/Kotlin? Om inte så är det på väg.

Dag 4 i Scala:

// Skapa regexes. Kunde gjort regex inline i Scala match-clausen, men regex Pattern instanser kompileras // på JVMen till Matchers. Så Matchers blir snabba, men kompileringen är en dyr operation och tvivlar // på att kompilatorn har en regel för att kolla om regex strängen är statisk och isf hoistar ut och bara // kompilerar en gång. val DIG_4 = "(\\d{4})".r val CM = "(\\d+)cm".r val IN = "(\\d+)in".r // Filteringsfunktionen. Eftersom regexarna har match group i sig så kan jag assigna en variabel // (exvis birthYear) till match groupen och sen använda variabeln i case kropparna. // Är inte konsekvent, använder in-line regex på dom 3 sista, skulle kunna gjort dom statiska som dom // ovan och även använt dom i case matchen och sen bara returnera "true" i kroppen men tyckte det blev // mer läsbart så här. Om det var riktig produktionskod med kritisk performance skulle jag gjort val Patterns // utanför funktionen här med. def validator(s: String): Boolean = { s.split(":") match { case Array("byr", DIG_4(birthYear)) => (1920 to 2002 contains birthYear.toInt) case Array("iyr", DIG_4(issueYear)) => (2010 to 2020 contains issueYear.toInt) case Array("eyr", DIG_4(expireYear)) => (2020 to 2030 contains expireYear.toInt) case Array("hgt", CM(height)) => (150 to 193 contains height.toInt) case Array("hgt", IN(height)) => (59 to 76 contains height.toInt) case Array("hcl", color) => color.matches("^\\#((\\d|[a-f]){6})") case Array("ecl", eye) => eye.matches("(amb)|(blu)|(brn)|(gry)|(grn)|(hzl)|(oth)") case Array("pid", pid) => pid.matches("\\d{9}") case _ => false } } val passports = inputFile.split("\\n\\n") //Tokenisera och validera val validValues = testPassports.map(_.split("\\s+").filter(validator) //Inte kollat om det finns duplicate keys i några passports, men för att vara säker validValues.map(a => a.distinctBy(_.take(3))).filter(_.size == 7).size

Jag dock borde ha castat till int först och sen gjort (if x < foo && x > bar) också så det blev typsäkert även om det blir mera svårläst. Slösade en halvtimme på att jag först missat att skriva birthYear.toInt så det blev bara (1920 to 2002 contains birthYear) och en sträng kan ju aldrig finnas i en Int range så då blev ju inget passport giltigt. Hade jag använt den mer typsäkra varianten hade den kastat exception "no operator '<' on String" om jag missat att skriva toInt så då hade jag hittat buggen direkt. Alternativt hade jag kunnat skriva unit tester men jag är alltid så noga med det i produktionskod så det känns befriande att slarva lite för en gångs skull

Dold text
Av wgren
Skrivet av adrimur:

Någon annan här som tar tillfället i akt att lära sig lite Haskell?

Dag 1:

Läser helt enkelt in talen i en lista "nums" och skriver ut produkten av paret som summerar till 2020 genom

print $ head [x*y | x <- nums, y <- nums, x+y==2020]

Måste använda "head" eftersom man ju hittar två par, y+x och x+y

Motsvarande för task 2 blir

print $ head [x*y*z | x <- nums, y <- nums, z <- nums, x+y+z==2020]

Dold text

Snyggt! Jag kör Scala. Haskell ligger på min todo lista, men det är en diger lista. Kanske bara tillfället i akt att lära mig Cats, ett categori-teori baserat library för ren funktionell programmering. Ganska Hanskell-inspirarerat misstänker jag.
https://typelevel.org/cats/

Min dag 1 task 2 blev likartad, typ

(for {
x <- nums
y <- nums
z <- nums
if (x+y+z == 2020)
} yield (Bigint(x) * BigInt(y) * BigInt(z))).head

Dold text

Dag 4 blev visst regex repetitionsdagen.