Idiomatische Möglichkeit, viele der gleichen Generatoren in einem Listenverständnis zu haben

8

Im Statistics-Kurs zeigte mir unser Lehrer ein Wahrscheinlichkeitsmodell aller möglichen Würfe mit zwei Würfeln, die zu 4 addiert wurden. Wenn ich daran denke, dass die Haskell-Listen-Comprehensions ziemlich großartig sind, habe ich beschlossen, den nächsten Schritt zu machen und diesen Code zu schreiben um alle möglichen Würfe mit 4 Würfeln zu finden, die zu 10 hinzugefügt werden :

[(d1,d2,d3,d4) | d1 <- [1..6], d2 <- [1..6], d3 <- [1..6], d4 <- [1..6], (d1 + d2 + d3 + d4) == 10]

Dies funktioniert wie erwartet und gibt mir die Ausgabe von

  

[(1,1,2,6), (1,1,3,5), (1,1,4,4), (1,1,5,3), (1,1,6 , 2), (1,2,1,6), (1,2,2,5), (1,2,3,4), (1,2,4,3), (1,2,5 , 2), (1,2,6,1), (1,3,1,5), (1,3,2,4), (1,3,3,3), (1,3,4 , 2), (1,3,5,1), (1,4,1,4), (1,4,2,3), (1,4,3,2), (1,4,4 , 1), (1,5,1,3), (1,5,2,2), (1,5,3,1), (1,6,1,2), (1,6,2 1), (2,1,1,6), (2,1,2,5), (2,1,3,4), (2,1,4,3), (2,1,5 , 2), (2,1,6,1), (2,2,1,5), (2,2,2,4), (2,2,3,3), (2,2,4 , 2), (2,2,5,1), (2,3,1,4), (2,3,2,3), (2,3,3,2), (2,3,4 , 1), (2,4,1,3), (2,4,2,2), (2,4,3,1), (2,5,1,2), (2,5,2 , 1), (2,6,1,1), (3,1,1,5), (3,1,2,4), (3,1,3,3), (3,1,4 , 2), (3,1,5,1), (3,2,1,4), (3,2,2,3), (3,2,3,2), (3,2,4 , 1), (3,3,1,3), (3,3,2,2), (3,3,3,1), (3,4,1,2), (3,4,2 , 1), (3,5,1,1), (4,1,1,4), (4,1,2,3), (4,1,3,2), (4,1,4 , 1), (4,2,1,3), (4,2,2,2), (4,2,3,1), (4,3,1,2), (4,3,2 , 1), (4,4,1,1), (5,1,1,3), (5,1,2,2), (5,1,3,1), (5,2,1 , 2), (5,2,2,1), (5,3,1,1), (6,1,1,2), (6,1,2,1), (6,2,1 , 1)]

Hier kommt meine Frage ins Spiel. Ruby ist ein großer Teil meines Hintergrunds, also bin ich sehr groß auf der TROCKENES Prinzip. % Co_de% in meinem Code zu haben, scheint unnötig zu sein, und ich möchte glauben, dass es einen besseren Weg gibt, dies zu tun.

Wie ich es verstehe, funktioniert meine aktuelle Methode, indem ich 4 verschachtelte Schleifen hinter den Kulissen ablaufen lasse - eine für jeden Generator. Gibt es eine Möglichkeit, den einen d1 <- [1..6], d2 <- [1..6], d3 <- [1..6], d4 <- [1..6] -Generator für alle Variablen zu verwenden, sodass effektiv 4 verschachtelte Schleifen erstellt werden? Wenn nicht, gibt es eine weniger redundante oder idiomatische Art, diesen Code zu schreiben, um das gleiche Ergebnis zu erzielen?

Hinweis: Ich bin ziemlich neu in der Sprache, also entschuldige ich mich, wenn dies etwas ist, das offensichtlich sein sollte. Wenn Sie irgendwelche Wörter / Konzepte verwenden, die einem unerfahrenen imperativ / objektorientierten Programmierer nicht geläufig sind, versuchen Sie bitte, sie für mich zu erklären.

    
Peril 18.10.2017, 16:08
quelle

4 Antworten

12

Wenn Sie sich für die [1..6] Wiederholung interessieren (die Fähigkeit, den Bereich unabhängig voneinander zu variieren), könnten Sie Folgendes verwenden:

%Vor%

Overall, um die explizite Die-Benennung zu entfernen, während dies nicht genau dasselbe ist, da es Listen statt Tupeln sein wird:

%Vor%

Um die Tupel wiederherzustellen, die Sie mit dem Muster vergleichen können, die jedoch möglicherweise Probleme bei der Suche nach Fehlern mit sich bringen, wenn sich der Eingabeausdruck bei fehlgeschlagenem Mustervergleich ändert, wird das Element einfach ausgeschlossen:

%Vor%     
ryachza 18.10.2017, 16:24
quelle
9

Wenn Sie bei 4-Tuples , d. h. (1,1,3,5) , bleiben möchten, ist das nicht wirklich elegant, aber wenn Sie bereit sind, Listen stattdessen zu verwenden, können Sie ziemlich elegant verwalten

%Vor%

oder mit do -notation

%Vor%     
epsilonhalbe 18.10.2017 16:24
quelle
2

edit2: Die idiomatische Möglichkeit, die Suche einzuschränken, während sie ausgeführt wird, besteht darin, möglichst früh zu testen, - so früh wie möglich, - um das zu reduzieren Suchraum so weit wie möglich:

%Vor%

Das Folgende ist ein anderer Ansatz für dieses Problem.

Hier geht es darum, eine Art Netzwerk von Datenprozessoren und Multiplikatoren zu schaffen, um das Ziel zu erreichen und Möglichkeiten zur Effizienzsteigerung zu nutzen. Um Doppelarbeit zu vermeiden, verwenden wir doppelte Daten, um eine logarithmische Höhe des Prozessketten / Datenpfade-Graphen anstelle einer linearen Höhe zu erreichen.

> %Vor%

Um aus all diesen Bits und Stücken eine richtige Funktion zu machen, müssen Sie herausfinden, wie Sie rn aus n erzeugen - indem Sie ihre Binärdarstellung verwenden, oder durch wiederholte Halbierung oder so etwas .

Für jetzt

  

~ & gt; foo r2
  [(4,6), (5,5), (6,4)]

  ~ & gt; nimm 10 $ foo r4
  [((1,1), (2,6)), ((1,1), (3,5)), ((1,1), (4,4)), ((1,1), (5,3)), ((1,1), (6,2)), ((1,2), (1   , (6), ((1,2), (2,5)), ((1,2), (3,4)), ((1,2), (4,3)), ((1 , 2), (5,2))]

  ~ & gt; nimm 10 $ foo r6
  [((1, (1,1)), (1, (1,5))) ((1, (1,1)), (1, (2,4))), ((1, ( 1,1), (1, (3,3))), ((1, (1,1)), (1   , (4,2))), ((1, (1,1)), (1, (5,1))), ((1, (1,1)), (2, (1,4) )), ((1, (1,1)), (2, (2,3))), ((1, (   1,1), (2, (3,2))), ((1, (1,1)), (2, (4,1))), ((1, (1,1)), (3, (1,3)))]

    
Will Ness 18.10.2017 17:23
quelle
2

Ich glaube, dass in Haskell ein idiomatischer Weg erreicht werden kann, indem der am besten geeignete Datentyp für diesen Würfelwürfelprozess gefunden wird.

Also was ist es ..? Wir werden n dice für m mal rollen. Dieser Algorithmus ruft nach einem Rose-Tree-Datentyp (N-yr-Baum). Wie zum Beispiel

%Vor%

Nun, Sie haben dies bereits in Data.Tree package, aber um die Logik in einfacheren Worten auszudrücken, werde ich versuchen, die oben genannten Daten sehr vereinfacht N-ary Baumtyp zu verwenden. Es ist trivial, dies mit dem Standard-Data.Tree-Paket durch Entfalten zu implementieren.

Auch das ist sehr effizient, da wir die unmöglichen Routen beseitigen, bevor sie passieren. Also keine ineffiziente Kombinatorik und Filterung hier.

%Vor%

Dies berechnet einen N-stufigen Baum mit m Ebenen und jeder Pfad enthält die Würfelwerte mit Ausnahme der Wurzel (Samen), die 0 ist. Lassen Sie uns das also für 4 Würfel mit einer Zielsumme von 10 sehen.

%Vor%

So oben haben wir die Baumdarstellung. Ich werde eine Funktion toList schreiben, sobald ich etwas Zeit habe.

    
Redu 23.10.2017 15:25
quelle