Çöđ*_*xěŕ 2 dictionary functional-programming elm
老实说,我对函数式编程还比较陌生,大约两天。我正在尝试从 a 打印出值Dict Int Int,但我似乎无法弄清楚如何将其传递Dict Int Int给函数。
我收到的错误如下。
第一个参数
map不是我所期望的:32| div[](Dict.map toLiDict dict) ^^^^^^^^ 这个
toLiDict值为a:Run Code Online (Sandbox Code Playgroud)Dict Int Int -> Html msg但
map第一个参数需要是:Run Code Online (Sandbox Code Playgroud)Dict Int Int -> b
函数toHtmlDict和toLiDict是导致我出现此问题的函数,它们目前已在下面注释掉。我也从视图中调用它,div [] [ toHtmlDict model.uniqueValues ]我也对此进行了注释。
这是我目前正在处理的工作;我发布了整个代码,因为如果您需要其他任何东西,它会更容易。
这里有一个关于 Ellie 的链接,可以运行。
module Main exposing (main)
import Browser
import Dict exposing (Dict)
import Html.Attributes
import Html exposing (Html, button, div, text, strong, p, li, ul)
import Html.Events exposing (onClick)
type alias Model =
{ currentNumber : Int, clicks : Int, outputList : List(String), uniqueValues : Dict Int Int }
--{ currentNumber : Int, clicks : Int, history : String, outputList : List(String) }
initialModel : Model
initialModel =
{ currentNumber = 0, clicks = 0, outputList = [""], uniqueValues = Dict.fromList [(1,1)] } --Dict.empty should be default here...
--{ currentNumber = 0, clicks = 0, history = "Current outputs ...", outputList = ["Current outputs ...", " "] }
-- applies a new div for each element in the list
toHtmlList : List String -> Html msg
toHtmlList strings =
div [] (List.map toLi strings)
-- creates a div along with the text to be shown
toLi : String -> Html msg
toLi s =
div [] [ text s ]
-- --applies a new div for each element in the dictionary
-- toHtmlDict : Dict Int Int -> Html msg
-- toHtmlDict dict =
-- div [] (Dict.map toLiDict dict)
-- -- creates a div along with the text to be shown
-- toLiDict : Dict Int Int -> Html msg
-- toLiDict k =
-- div [] [ text "What here?" ]
type Msg
= Increment
| Decrement
update : Msg -> Model -> Model
update msg model =
case msg of
-- Note: when assigning the model.currentNumber and then concatenating the value, it will not be updated...
Increment ->
{ model | currentNumber = model.currentNumber + 1, clicks = model.clicks + 1, outputList = model.outputList ++ [addToPage (oddOrEven(model.currentNumber + 1)) (model.currentNumber + 1)], uniqueValues = model.uniqueValues }
--{ model | currentNumber = model.currentNumber + 1, clicks = model.clicks + 1, history = model.history ++ addToPage (oddOrEven(model.currentNumber + 1)) (model.currentNumber + 1), outputList = model.outputList ++ [addToPage (oddOrEven(model.currentNumber + 1)) (model.currentNumber + 1)] }
Decrement ->
{ model | currentNumber = model.currentNumber - 1, clicks = model.clicks + 1, outputList = model.outputList ++ [addToPage (oddOrEven(model.currentNumber - 1)) (model.currentNumber - 1)]}
--{ model | currentNumber = model.currentNumber - 1, clicks = model.clicks + 1, history = model.history ++ addToPage (oddOrEven(model.currentNumber - 1)) (model.currentNumber - 1), outputList = model.outputList ++ [addToPage (oddOrEven(model.currentNumber - 1)) (model.currentNumber - 1)]}
view : Model -> Html Msg
view model =
Html.div []
[ button [ onClick Increment ] [ strong [Html.Attributes.style "color" "black"] [ text "+1" ]]
, button [ onClick Decrement ] [ strong [Html.Attributes.style "color" "red"] [ text "-1" ]]
, p [] []
--, div [] [ text <| "The current number is: ", strong [Html.Attributes.style "color" "red"] [ text <| String.fromInt model.currentNumber ], text " and it's ", text (oddOrEven model.currentNumber) ]
, div [] [ text <| "The current number is: ", strong [Html.Attributes.style "color" <| evenOddColor model.currentNumber] [ text <| String.fromInt model.currentNumber ], text " and it's ", strong [Html.Attributes.style "color" <| evenOddColor model.currentNumber ] [ text <| oddOrEven model.currentNumber ] ]
, div [] [ text "Total clicks: ", strong [Html.Attributes.style "color" "red"] [ text <| String.fromInt model.clicks ]]
, p [] []
, div [] [ strong [Html.Attributes.style "color" "Blue"] [ text "Unique values ..." ]]
, p [] []
--, div [] [ toHtmlDict model.uniqueValues ]
--, div [] [ text <| "The current number is: " ++ String.fromInt model.currentNumber ++ " and it's " ++ oddOrEven model.currentNumber ]
--, div [] [ text "Total clicks: ", strong [Html.Attributes.style "color" "red"] [ text <| String.fromInt model.clicks ]]
--, p [] []
, div [] [ strong [Html.Attributes.style "color" "Blue"] [ text "History ..." ]]
, p [] []
--, div [] [ text <| model.history ] - TEMPORARY
, div [] [ toHtmlList model.outputList ]
]
-- appendToList string number =
-- "Number was " ++ String.fromInt number ++ " and it was " ++ string
addToPage string number =
"Number was " ++ String.fromInt number ++ " and it was " ++ string
-- determines if number is even or odd
oddOrEven number =
if modBy 2 number == 0 then
"even"
else
"odd"
-- call another function with param
evenOddColor number =
if oddOrEven(number) == "even" then
"green"
else
"red"
main : Program () Model Msg
main =
Browser.sandbox
{ init = initialModel
, view = view
, update = update
}
Run Code Online (Sandbox Code Playgroud)
Dict.map是一个函数,它将转换 a 的值Dict并返回另一个Dict具有新值的函数。这不是您想要的,但无论如何,让我们尝试了解它的类型,因为这是一次有用的学习体验。
ADict的完整类型是Dick k v,这意味着类型变量分别对应于其键和值的类型。Dict.map根据文档,有类型(k -> a -> b) -> Dict k a -> Dict k b. 作为它的第一个参数,它采用两个参数的函数k和a,并返回一个b。maps 第二个参数是 a Dict k a,它返回 a Dict k b。
我们可以看到kinput 和 return 中的 是相同的Dict,这意味着它的类型将保持相同,但值的类型变量a在 input 和breturn 中是不同的Dict。该函数同样将aa 作为其输入之一,k并返回 a b。因此,对于 input 中的每个键值对Dict,将使用其键“k”和值“a”调用映射函数,并预计返回一个b值。
对于 aDict Int Int如你所拥有的那样, 和k都是vs Int。Dict.map如果我们用我们得到的类型替换它们(Int -> Int -> b) -> Dict Int Int -> Dict Int b。我们还不知道b是什么,因为这将由我们传递给它的函数决定,但我们至少可以看到该函数需要两个Int参数。
同时,您提供给它的函数 的类型toLiDict采用Dict Int Int -> Html msg一个不是 的参数Int。这就是错误消息笨拙地试图传达的内容。我们可以重写toLiDict以符合Dict.map预期,作为一个函数Int -> Int -> Html msg,但这会Dict.map返回一个Dict Int (Html msg),这不是你想要的。所以让我们放弃这个吧。
一般来说,map函数通常会转换集合的元素,而不改变集合的类型。
如果您想要将集合的元素完全转换为其他东西,并且没有更具体的东西可以使用,那么“折叠”通常是正确的工具。Dict提供foldl和foldr,它基本上执行相同的操作,但顺序不同,从“左”和“右”foldl迭代元素。foldr如果顺序不重要,请使用,foldl因为它更有效。
的类型签名Dict.foldl是(k -> v -> b -> b) -> b -> Dict k v -> b. 也就是说,转换函数现在接受三个参数:键、k值、v和 a b(我们将其称为累加器),并返回 a b。foldl它本身还需要一个附加参数,同样是 a b,它将作为b传递给转换函数的初始值。第三个参数是Dict,返回值也是 a b。
对于输入中的每个键值对Dict,foldl将像 一样map,使用其键和值调用转换函数。但它还提供了一个b最初是b传递给foldl自身的值,但对于后续迭代将是b从转换函数返回的值。通过这种方式,“累加器”累加返回值。
让我们重写您的代码以Dict.foldl代替使用:
toHtmlDict : Dict Int Int -> Html msg
toHtmlDict dict =
div [] (Dict.foldl toLiDict [] dict)
toLiDict : Int -> Int -> List (Html msg) -> List (Html msg)
toLiDict k v acc =
div [] [ text "What here?" ] :: acc
Run Code Online (Sandbox Code Playgroud)
这里,toHtmlDict基本上保持不变,但使用Dict.foldl代替Dict.map并为其提供一个空列表的初始值[]。
toLiDict看到更大的变化。它的类型已更改为Int -> Int -> List (Html msg) -> List (Html msg),这意味着它接受参数:键和值,两者都是Ints,累加器是 a List (Html msg),返回值也是。
但实施方式几乎没有改变。它不是直接返回一个元素,而是使用 附加到累加器:: acc。
这就是全部内容。正如预期的那样,折叠的结果是累积的Html元素列表。如果你将上面的代码放入你的代码中,它就会起作用。
最后,我之前指出,foldl如果没有更合适的专门功能,这是一个不错的选择。由于您想要的最终结果是一个列表,或者Dict.values或Dict.toList,正如 @bdukes 所建议的那样,可能是。这些不如折叠那么有效,因为您将遍历元素两次,一次转换为列表,然后映射它们,但这在实践中很少重要。专用函数也更具描述性,可以更好地记录您的意图,因此如果可以的话请使用它们。