Ошибка типа в функции, переданной в Dict.map - PullRequest
1 голос
/ 24 мая 2019

Я довольно новичок в функциональном программировании, если честно, около 2 дней. Я пытаюсь распечатать значения из Dict Int Int, но не могу понять, как передать это Dict Int Int в функцию.

Я получаю сообщение об ошибке ниже.

1-й аргумент map не тот, который я ожидаю:

32 | div [] (Dict.map toLiDict dict) ^^^^^^^^ Это toLiDict значение равно:

Dict Int Int -> Html msg

Но map нужен 1-й аргумент:

Dict Int Int -> b

Функции toHtmlDict и toLiDict вызывают у меня эту проблему, они в настоящее время закомментированы ниже. Также я называю это с точки зрения, div [] [ toHtmlDict model.uniqueValues ], я тоже это закомментировал.

Вот с чем я сейчас работаю; Я разместил весь код, так как было бы проще, если бы вам понадобилось что-нибудь еще.

Вот ссылка на Элли здесь , которую можно запустить.

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 ]

3 голосов
/ 24 мая 2019

Dict.map - это функция, которая преобразует значения Dict и возвращает еще одно Dict с новыми значениями. Это не то, что вам нужно, но давайте все равно попробуем использовать его тип, потому что это полезный опыт обучения.

Dict.map

A Dict s полный тип равен Dick k v, что означает, что переменные типа соответствуют типу его ключей и значений соответственно. Dict.map, согласно документации , имеет тип (k -> a -> b) -> Dict k a -> Dict k b. В качестве первого аргумента он принимает функцию из двух аргументов k и a, которая возвращает b. Второй аргумент map - это Dict k a, и он возвращает Dict k b.

Мы можем видеть, что k одинаков как для ввода, так и для возврата Dict с, что означает, что его тип останется прежним, но переменная типа для значений отличается, a на входе и 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

Если вместо этого вы хотите полностью преобразовать элементы коллекции во что-то еще, и не существует чего-то более специфичного для использования, «складывание» обычно является правильным инструментом. Dict обеспечивает foldl и foldr, что в основном делает то же самое, но в другом порядке, foldl перебирает элементы «слева» и foldr «справа». Если порядок не имеет значения, используйте foldl, потому что он более эффективен.

Подпись типа Dict.foldl является (k -> v -> b -> b) -> b -> Dict k v -> b. То есть функция преобразования теперь принимает три аргумента: ключ k, значение v и b, которые мы назовем аккумулятором, и возвращает 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 элементов, как и ожидалось. Если вы добавите приведенный выше код в свой, он будет работать.

Dict.values ​​и Dict.toList

Наконец, ранее я заметил, что foldl - хороший выбор, если нет более подходящей специализированной функции.А поскольку конечный результат, который вам нужен, это список, либо Dict.values, либо Dict.toList, как предположил @bdukes, вероятно, так оно и есть.Они не так эффективны, как фолд, так как вы будете повторять элементы дважды, один раз для преобразования в список, а затем для их отображения, но на практике это редко имеет значение.Специализированные функции также более наглядны и лучше документируют ваши намерения, поэтому используйте их, если можете.

2 голосов
/ 24 мая 2019

Определение 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) ]
...