2012-08-04 14 views
10

Ähnliche: Haskell Random from DatatypeGenerieren einen Zufallswert von einem benutzerdefinierten Datentyp in Haskell

Ich habe einen Datentyp erstellt, die verschiedenen Waffen im Rock Paper Scissor Spiel enthält.

Jetzt möchte ich eine zufällige Waffe generieren, die vom Computer gegen den Benutzer verwendet wird. Ich habe einen ähnlichen Link, den ich am Anfang gepostet habe, aber es scheint mir zu allgemein.

Ich bin in der Lage, Zufallszahlen von jedem anderen Typ zu generieren. Worum ich mich kümmern kann, ist, wie ich meinen Datentyp zu einer Instanz der Random-Klasse machen kann.

+1

verwenden können [Was haben Sie versucht?] (Http://www.whathaveyoutried.com) –

Antwort

14

Um ein zufälliges Weapon zu erzeugen, ob Sie Weapon eine Instanz von Random oder nicht machen, was Sie brauchen, ist eine Möglichkeit, Zahlen Weapon s abzubilden. Wenn Sie Enum für den Typ ableiten, wird eine Zuordnung von und zu Int s vom Compiler definiert. So könnten Sie zum Beispiel

definieren. Mit einem Enum Beispiel können Sie auch machen Weapon leicht eine Instanz Random:

instance Random Weapon where 
    random g = case randomR (0,2) g of 
       (r, g') -> (toEnum r, g') 
    randomR (a,b) g = case randomR (fromEnum a, fromEnum b) g of 
         (r, g') -> (toEnum r, g') 

Wenn es eine Möglichkeit, das Hinzufügen oder Entfernen von Konstrukteuren von dem Typ ist, der beste Weg, die Grenzen für randomR synchron mit der halten Typ ist auch Bounded ableiten, wie Joachim Breitnerimmediately suggested:

data Weapon 
    = Rock 
    | Paper 
    | Scissors 
     deriving (Bounded, Enum) 

instance Random Weapon where 
    random g = case randomR (fromEnum (minBound :: Weapon), fromEnum (maxBound :: Weapon)) g of 
       (r, g') -> (toEnum r, g') 
    randomR (a,b) g = case randomR (fromEnum a, fromEnum b) g of 
         (r, g') -> (toEnum r, g') 
+6

mit einer zusätzlichen abgeleiteten Bounded Zum Beispiel können Sie den Code komplett abstrakt machen. –

+0

Was passiert, wenn ich eine neue Waffe hinzufüge?Gibt es eine allgemeine Möglichkeit, die Anzahl verschiedener Werte zu kennen, die ich als Datentyp haben kann? – Oni

+0

Dann ist auch das Ableiten von "Bounded" der einfachste Weg, Dinge synchron zu halten. Antwort aktualisieren –

8

Während Daniel Fischer Enum Ansatz ist sicherlich eine gute allgemeine Art und Weise, dies zu tun, ist es nicht wirklich notwendig, eine explizite Zuordnung von Int s zu verwenden. Sie können auch tun, nur

instance Random Weapon where 
    random g = case random g of 
       (r,g') | r < 1/3 = (Rock , g') 
         | r < 2/3 = (Paper , g') 
         | otherwise = (Scissors, g') 

die Double Instanz Random verwenden. Dies ist weniger effizient als bei einer Enum Instanz abgeleitet, aber flexibler - zum Beispiel, Sie leicht eine ungleiche Verteilung

random g = case random g of 
       (r,g') | r < 1/4 = (Rock , g') 
         | r < 1/2 = (Paper , g') 
         | otherwise = (Scissors, g') 
als die beiden anderen

wo Scissors wahrscheinlicher ist, definieren könnte. Das sollten Sie natürlich nur dann tun, wenn die Nicht-Gleichverteilung für Ihren Datentyp in gewisser Weise kanonisch ist, schon gar nicht in diesem Beispiel.

+0

Das ist sehr nett und lesbar. –

+0

['randomR'] (https://hackage.haskell.org/package/random-1.1/docs/System-Random.html#v:randomR) sagt, es erwartet eine gleichmäßige Verteilung unter den möglichen Ausgaben. Für eine ungleichmäßige Verteilung ist es wahrscheinlich am besten, eine separate Funktion zu implementieren, um zu vermeiden, dass dieser Typklassenvertrag gebrochen wird. – 4castle

+0

@ 4castle, dass der typeclass-Vertrag sowieso nicht wirklich hält. Für z.B. "Doppelt", "einheitlich" wird im Sinne einer kontinuierlichen Verteilung verstanden - d. H. Die Wahrscheinlichkeitsdichte in der realen Linie ist konstant. Ist sicher sinnvoll. Die reelle Linie wird jedoch nicht einheitlich mit Gleitkommawerten abgetastet. Wenn Sie sich also die Verteilung der binären einzelnen "Double" -Werte ansehen, würden Sie bemerken, dass "0.8346253987632499" viel häufiger vorkommt als "0.0000003265368256732462 '. – leftaroundabout

5
{-# LANGUAGE FlexibleInstances, UndecidableInstances, 
ScopedTypeVariables, OverlappingInstances #-} 

import System.Random 

class (Bounded a, Enum a) => BoundedEnum a 
instance (Bounded a, Enum a) => BoundedEnum a 
instance BoundedEnum a => Random a where 
    random gen = randomR (minBound :: a, maxBound :: a) gen 
    randomR (f, t) gen = 
    (toEnum r :: a, nextGen) 
    where 
     (rnd, nextGen) = next gen 
     r = fromEnum f + (rnd `mod` length [f..t]) 

Jetzt können Sie sagen:

r <- randomIO :: Anything 

wo Alles muss eine Instanz der Klassen Enum und Bounded sein.

+4

Was ist der Grund für die Definition von 'BoundedEnum' und nicht nur die direkte Verwendung von' (Bounded a, Enum a) => Random a'? – huon

2

Wenn Sie einen Generator für die Daten dann Sie oneof von Test.QuickCheck.Gen

Verwandte Themen