老实说,我是函数式编程的新手,大约两天了。我正在尝试从Dict Int Int
中打印出值,但似乎无法弄清楚如何将此Dict Int Int
传递给函数。
我收到的错误如下。
map
的第一个参数不是我期望的:32 | div [](将Dict.map映射到LiDict dict) ^^^^^^^^^此
toLiDict
值是:Dict Int Int -> Html msg
但是
map
需要第一个参数为:Dict Int Int -> b
函数toHtmlDict
和toLiDict
是导致我遇到此问题的函数,目前在下面将其注释掉。我也是从div [] [ toHtmlDict model.uniqueValues ]
的角度来称呼它,我也对此做了注释。
这是我目前正在使用的东西;我发布了整个代码,因为如果您还需要其他任何内容,它将变得更加容易。
以下是可运行的Ellie here上的链接。
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
}
答案 0 :(得分:3)
Dict.map
是一个函数,它将转换Dict
的值并返回带有新值的另一个Dict
。这不是您想要的,但是无论如何都要尝试使用它的类型,因为这是一种有用的学习体验。
Dict
的完整类型为Dick k v
,这意味着类型变量分别对应于其键和值的类型。根据{{3}},Dict.map
的类型为(k -> a -> b) -> Dict k a -> Dict k b
。作为第一个参数,它具有两个参数k
和a
的功能,并返回一个b
。 map
的第二个参数是Dict k a
,它返回一个Dict k b
。
我们可以看到k
在输入Dict
和返回a
中都是相同的,这意味着其类型将保持不变,但是值的类型变量是不同的{{ 1}}输入中,b
返回Dict
中。同样,该函数将a
与k
一起作为其输入之一,并返回b
。因此,对于输入Dict
中的每个键值对,映射函数将使用其键'k'和值'a'进行调用,并期望返回一个b
值
对于您拥有的Dict Int Int
,k
和v
均为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
遍历“ left”元素和foldr
中的元素正确的”。如果顺序无关紧要,请使用foldl
,因为这样更有效。
Dict.foldl
的类型签名为(k -> v -> b -> b) -> b -> Dict k v -> b
。也就是说,转换函数现在使用三个参数,即键k
,值v
和一个b
(我们将其称为累加器),并返回一个{{1} }。 b
本身也接受一个附加参数,再次为foldl
,它将是传递给转换函数的初始b
值。第三个参数是b
,返回值再次是Dict
。
对于输入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
基本上保持不变,但使用toHtmlDict
而不是Dict.foldl
,并为其提供了一个空列表Dict.map
的初始值。
[]
看到了更大的变化。它的类型已更改为toLiDict
,并带有参数:键和值,两者均为Int -> Int -> List (Html msg) -> List (Html msg)
,累加器为Int
,返回值也是如此。
但是实现几乎没有改变。不仅直接返回元素,还使用List (Html msg)
将其附加到累加器。
仅此而已。折叠的结果是:: acc
元素的累积列表,如预期的那样。如果将上面的代码放入您的代码中,它将起作用。
最后,我之前提到过Html
是一个不错的选择,如果没有更合适的专门功能。而且由于您想要的最终结果是一个列表,所以正如{bdukes所建议的那样,可能是foldl
或Dict.values
。这些效率不如折叠,因为您将遍历元素两次,一次转换为列表然后映射它们,但实际上这并不重要。专用功能也更具描述性,并且可以更好地记录您的意图,因此请尽可能使用它们。
答案 1 :(得分:2)
Dict.map
的定义为(k -> a -> b) -> Dict k a -> Dict k b
。因此它需要一个函数和一个Dict
并返回一个新的Dict
。该映射函数采用键和值,并返回一个新值。
对于您而言,您希望返回的是List (Html Msg)
,而不是任何内容的Dict
。因此,我将不使用Dict.map
,而是调用Dict.values
来获取值List
,然后使用List.map
将这些值转换为Html Msg
。如果同时需要键和值来生成Html Msg
,请改用Dict.toList
,以获得List (k, v)
(即List
个元组,其中元组具有键)和价值)。
toHtmlDict : Dict Int Int -> Html Msg
toHtmlDict dict =
div [] (List.map viewDictEntry (Dict.toList dict))
viewDictEntry : (Int, Int) -> Html Msg
viewDictEntry (key, value) =
li [] [ text (String.fromInt key), text " = ", text (String.fromInt value) ]