2010-05-28 18 views
17

Quiero hacer una función Haskell que pueda seleccionar un número aleatorio de una lista dada. Mi tipo de firma es:¿Cómo elegir un elemento de lista aleatorio en una función pura?

randomPick :: [a] -> a 

¿Qué debo hacer?

+1

¿Por qué quieres hacer esto? Si nos lo dices, quizás podamos darte una respuesta más útil que (la verdad) "no puedes". –

+15

aquí tienes: 'randomPick = (!! 7)' (Elegí el número 7 totalmente al azar) – yairchu

+2

@yairchu: Buena elección, ya que [los números que terminan en 7 son de hecho muy aleatorios] (http://scienceblogs.com /cognitivedaily/2007/02/is_17_the_most_random_number.php). Ver también [esta implementación posible] (http://www.random.org/analysis/dilbert.jpg) para un RNG referencialmente transparente. –

Respuesta

22

Lo que has descrito no se puede hacer en código funcional puro.

El código de función pura implica que obtendrá la misma salida para la misma entrada, siempre. Como una función aleatorizada, por definición, le da salida diferente para la misma entrada, esto es imposible en el código funcional puro.

A menos que pase alrededor de un valor adicional como se explica en @camccann's answer. Técnicamente, ni siquiera tiene que ser tan avanzado como un RNG, según tus necesidades. Podría pasar alrededor de un número entero, y multiplicarlo por 10 y restar 3 (o lo que sea), luego tome el módulo de eso para encontrar su índice. Entonces su función permanece pura, pero usted controla directamente la aleatoriedad.

Otra opción es usar RandomRIO para generar un número en un rango, que luego puede usar para seleccionar un índice de la lista. Esto requerirá que ingrese la mónada IO.

+0

puedo hacer otra función como es: randomChoices :: [a] -> un randomChoices x = randomIO (XS makeIO) makeIO :: [a] -> IO [a] makeIO = error "ninguna" randomIO IO :: [a] -> a randomIO = error " ninguno " si es así cómo? – user345662

+1

No, eso tampoco es posible; mientras 'makeIO :: [a] -> IO [a]' es simplemente 'return', no hay función con tipo' IO a -> a'-no hay forma de dejar la mónada de IO una vez que la haya ingresado. Esto se debe a que, como dijo Mark, Haskell es puro: no puede cambiar los valores de las variables, por lo que cada vez que se le da una entrada a una función, siempre debe devolver el mismo resultado. Dado que 'IO' es una forma pura de representar acciones que, cuando se toman, pueden (parecer que) mutar cosas, debe ser indestructible, y esto se impone porque su definición está oculta y la falta de una función de' IO a -> a'. –

25

Parte de la definición de una función "pura" en Haskell es que es referentially transparent, es decir, intercambiable con el resultado de evaluarla. Esto implica que el resultado de evaluarlo debe ser el mismo cada vez. Ergo, la función que quieres no es posible, me temo. Para generar números aleatorios en Haskell, una función que tiene que hacer una de dos cosas:

tomar y devolver un generador de números pseudoaleatorios, por ejemplo:

randomPick :: RNG -> [a] -> (a, RNG) 

O utilice IO acceder a la aleatoriedad del "mundo exterior" :

randomPick :: [a] -> IO a 

Ambos estilos son provistos por the module System.Random. Además, en el primer caso, se puede abstraer el PRNG utilizando la mónada State, o quizás un special-purpose Random monad.

+4

Sí, veo MonadRandom (vinculado en esta respuesta) - Vine aquí específicamente para mencionar que si no estaba ya aquí. –

+2

También puede usar la mónada "Gen" de QuickCheck como una práctica implementación preparada. Entre otras cosas, tiene la función requerida ya implementada. –

5

Si desea utilizar generadores de números aleatorios en código puramente funcional pero no tiene que pasar explícitamente el estado del generador, puede utilizar la mónada de estado (o transformador de mónada) y ocultar las tuberías. Las mónadas de estado siguen siendo referencialmente transparentes y es seguro & normal escapar de una mónada de estado. También puede usar la mónada ST si desea una verdadera caja de seguridad mutable que sea puramente funcional en el exterior.

Aquí hay un código de utilidad que escribí y utilizo a veces:

rand :: (Random a, RandomGen g, MonadState g m) => a -> a -> m a 
rand lo hi = do 
    r <- get 
    let (val, r') = randomR (lo, hi) r 
    put r' 
    return val 
Cuestiones relacionadas