传递给Dict.map的函数的类型错误

时间:2019-05-24 15:01:11

标签: dictionary functional-programming elm

老实说,我是函数式编程的新手,大约两天了。我正在尝试从Dict Int Int中打印出值,但似乎无法弄清楚如何将此Dict Int Int传递给函数。

我收到的错误如下。

  

map的第一个参数不是我期望的:

     

32 | div [](将Dict.map映射到LiDict dict)                          ^^^^^^^^^此toLiDict值是:

Dict Int Int -> Html msg
     

但是map需要第一个参数为:

Dict Int Int -> b

函数toHtmlDicttoLiDict是导致我遇到此问题的函数,目前在下面将其注释掉。我也是从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
        }

2 个答案:

答案 0 :(得分:3)

Dict.map是一个函数,它将转换Dict的值并返回带有新值的另一个Dict。这不是您想要的,但是无论如何都要尝试使用它的类型,因为这是一种有用的学习体验。

Dict.map

Dict的完整类型为Dick k v,这意味着类型变量分别对应于其键和值的类型。根据{{​​3}},Dict.map的类型为(k -> a -> b) -> Dict k a -> Dict k b。作为第一个参数,它具有两个参数ka的功能,并返回一个bmap的第二个参数是Dict k a,它返回一个Dict k b

我们可以看到k在输入Dict和返回a中都是相同的,这意味着其类型将保持不变,但是值的类型变量是不同的{{ 1}}输入中,b返回Dict中。同样,该函数将ak一起作为其输入之一,并返回b。因此,对于输入Dict中的每个键值对,映射函数将使用其键'k'和值'a'进行调用,并期望返回一个b

对于您拥有的Dict Int Intkv均为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

相反,如果您想要将集合的元素完全转换为其他元素,并且没有更具体的用途,通常使用“折叠”是正确的工具。 Dict提供foldlfoldr,它们基本上做相同的事情,但顺序不同,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元素的累积列表,如预期的那样。如果将上面的代码放入您的代码中,它将起作用。

Dict.values和Dict.toList

最后,我之前提到过Html是一个不错的选择,如果没有更合适的专门功能。而且由于您想要的最终结果是一个列表,所以正如{bdukes所建议的那样,可能是foldlDict.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) ]