Problem 23
Extract a given number of randomly selected elements from a list.
Example
randomSelect seed 3 ["Al", "Biff", "Cal", "Dee", "Ed", "Flip"] == ["Cal", "Dee", "Al"]
You must use Elm's Random to implement randomSelect
. Use Random.step to generate a pseudo-random number. Random.step
takes a Generator and a Seed. The seed is passed as a parameter to randomSelect
. You will need to create a generator such as Random.int.
Random.step
will return both a randomly generated value from the generator, and a new seed. You must use the new seed for subsequent random numbers.
Unit Test
This unit test uses the Elm Architecture to allow a pseudo-random seed to enter into the pure functional world of Elm. You don't need to understand how this works to implement randomSelect
. Because it uses the Elm Architecture it will not run on elm.org/try. Instead, install Elm, compile and run this application on your local machine. See https://guide.elm-lang.org/install.html, or http://elmprogramming.com/getting-started-intro.html.
module Main exposing (..)
import Html exposing (Html, button, div, h2, p, text)
import Html.Events exposing (..)
import List exposing (drop, length, take)
import Random exposing (Generator, Seed, initialSeed, int, step)
randomSelect : Random.Seed -> Int -> List a -> ( List a, Random.Seed )
randomSelect seed n list =
-- your implementation goes here
( [], seed )
-- Main
main : Program Never Model Msg
main =
Html.program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
-- MODEL
type alias Model =
{ intSeed : Int
, tested : Bool
, failedCount : Int
}
init : ( Model, Cmd Msg )
init =
( Model 1 False 0, Cmd.none )
-- UPDATE
type Msg
= Test
| NewFace Int
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Test ->
( model, Random.generate NewFace (Random.int Random.minInt Random.maxInt) )
NewFace newSeed ->
( Model newSeed True (test model.intSeed), Cmd.none )
-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions model =
Sub.none
-- VIEW
view : Model -> Html Msg
view model =
div []
[ h2 [] [ text (testMsg model.tested model.failedCount) ]
, p [] [ text ("Seed value: " ++ (toString (model.intSeed))) ]
, p [] [ text ("Your die roll is " ++ (toString (Tuple.first (randomSelect (Random.initialSeed model.intSeed) 1 (1..6))))) ]
, button [ onClick Test ] [ text "Test" ]
]
{-| return the number of failed tests
-}
test : Int -> Int
test intSeed =
let
seed =
Random.initialSeed intSeed
( l1, s1 ) =
randomSelect seed 3 (1..1000)
( l2, s2 ) =
randomSelect seed 3 (1..1000)
( l3, s3 ) =
randomSelect s2 3 (1..1000)
( l4, s4 ) =
randomSelect s3 9 (1..9)
( l5, s5 ) =
randomSelect s4 3 [ "a", "b" ]
( l6, s6 ) =
randomSelect s5 0 [ 'a', 'b' ]
( l7, s7 ) =
randomSelect s6 -1 [ 'a', 'b' ]
( l8, s8 ) =
randomSelect s6 1 [ ]
in
List.length <|
List.filter ((==) False)
[ List.sort l1 == List.sort l2
, l2 /= l3
-- a billion to one that this won't match
, List.sort l4 == 1..9
, List.sort l5 == [ "a", "b" ]
, l6 == []
, l7 == []
, l8 == []
]
testMsg : Bool -> Int -> String
testMsg tested failedCount =
if tested then
case failedCount of
0 ->
"Your implementation passed all tests."
1 ->
"Your implementation failed one test."
x ->
"Your implementation failed " ++ (toString x) ++ " tests."
else
"Click the test button below"
(..) : Int -> Int -> List Int
(..) start end =
List.range start end
Hints
elementAt
anddropAt
from Problem 3 and Problem 20 could prove useful.- A more Elm-ish solution would be to define a new
Random.Generator
.