Hasta ahora solo hemos visto comandos para realizar solicitudes HTTP, pero también podemos ordenar otras cosas, como generar valores aleatorios. Vamos a crear una aplicación que tira dados y produce un número aleatorio entre 1 y 6.
import Browser
import Html exposing (..)
import Html.Events exposing (..)
import Random
-- MAIN
main =
Browser.element
{ init = init
, update = update
, subscriptions = subscriptions
, view = view
}
-- MODEL
type alias Model =
{ dieFace : Int
}
init : () -> (Model, Cmd Msg)
init _ =
( Model 1
, Cmd.none
)
-- UPDATE
type Msg
= Roll
| NewFace Int
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
Roll ->
( model
, Random.generate NewFace (Random.int 1 6)
)
NewFace newFace ->
( Model newFace
, Cmd.none
)
-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions model =
Sub.none
-- VIEW
view : Model -> Html Msg
view model =
div []
[ h1 [] [ text (String.fromInt model.dieFace) ]
, button [ onClick Roll ] [ text "Roll" ]
]
La novedad es el comando que se ejecuta en la función de actualización:
Random.generate NewFace (Random.int 1 6)
La generación de valores aleatorios funciona de forma ligeramente diferente a la de lenguajes como JavaScript, Python, Java, etc. ¡Veamos cómo funciona en Elm!
Para ello, utilizamos el paquete elm/random, en particular el módulo Random.
La idea principal es que contamos con un generador aleatorio que describe cómo generar un valor aleatorio. Por ejemplo:
import Random
probability : Random.Generator Float
probability =
Random.float 0 1
roll : Random.Generator Int
roll =
Random.int 1 6
usuallyTrue : Random.Generator Bool
usuallyTrue =
Random.weighted (80, True) [ (20, False) ]
Aquí tenemos tres generadores aleatorios. El generador "roll" indica que producirá un entero, y más específicamente, un entero entre 1 y 6 inclusive. Del mismo modo, el generador "usuallyTrue" indica que producirá un booleano, y más específicamente, será verdadero el 80% de las veces.
La cuestión es que todavía no estamos generando los valores. Simplemente estamos describiendo cómo generarlos. A partir de ahí, se usa "Random.generate" para convertirlo en un comando:
generate : (a -> msg) -> Generator a -> Cmd msg
Al ejecutar el comando, el generador genera un valor que se convierte en un mensaje para la función de actualización. En nuestro ejemplo, el generador genera un valor entre 1 y 6, que se convierte en un mensaje como NewFace 1 o NewFace 4. Eso es todo lo que necesitamos para obtener nuestras tiradas de dados aleatorias, ¡pero los generadores pueden hacer mucho más!
Una vez que tengamos generadores simples como probability y usuallyTrue, podemos empezar a combinarlos con funciones como map3. Imaginemos que queremos crear una máquina tragamonedas sencilla. Podríamos crear un generador como este:
import Random
type Symbol = Cherry | Seven | Bar | Grapes
symbol : Random.Generator Symbol
symbol =
Random.uniform Cherry [ Seven, Bar, Grapes ]
type alias Spin =
{ one : Symbol
, two : Symbol
, three : Symbol
}
spin : Random.Generator Spin
spin =
Random.map3 Spin symbol symbol symbol
Primero creamos un símbolo para describir las imágenes que pueden aparecer en la máquina tragamonedas. Luego, creamos un generador aleatorio que genera cada símbolo con la misma probabilidad.
A partir de ahí, usamos map3 para combinarlos en un nuevo generador de giros. Este indica que se deben generar tres símbolos y luego combinarlos para formar un giro.
La idea es que, a partir de pequeños bloques, podemos crear un generador que describe un comportamiento bastante complejo. Y luego, desde nuestra aplicación, solo tenemos que ejecutar algo como Random.generate NewSpin spin para obtener el siguiente valor aleatorio.
