如何使 input[][] return 成为 Elm 中的 Just Int 而不是 String?
How to make input[][] return a Just Int instead of String in Elm?
我是 Elm 的完全初学者,我正在为类型而苦苦挣扎。我一直在与类型作斗争,但我找不到解决此问题的便捷方法。有问题的代码是这样的:
view model =
div []
[ h1 [] [ text "Robot" ]
, input [ onInput SetX, value model.x ] []
, input [ onInput SetY, value model.y ] []
, input [ type_ "Int" , onInput SetCommands, value model.x] []
, button [ onClick ButtonPressed] []
, input [ readonly True ] []
]
X 和 Y 是在以下函数中使用的整数:
execute_orders : Int -> Int -> String -> String -> String -> { x : Int, y : Int, dir : String }
execute_orders x y commands lang dir =
let
moves =
case lang of
"English" ->
"RLF"
"French" ->
"HVG"
"Swedish" ->
"DGT"
_ ->
Debug.todo "No Language Found"
fw =
right 1 moves
directions =
facing dir
rght =
left 1 moves
lft =
slice 1 2 moves
first_move =
left 1 (toUpper commands)
rest =
slice 1 (length commands) (toUpper commands)
in
if first_move == "" then
{ x = x, y = y, dir = fromJust (head directions) }
else if first_move == fw then
if dir == "N" then
execute_orders x (y - 1) rest lang dir
else if dir == "E" then
execute_orders (x + 1) y rest lang dir
else if dir == "S" then
execute_orders x (y + 1) rest lang dir
else
execute_orders (x - 1) y rest lang dir
else if first_move == lft then
execute_orders x y rest lang (fromJust (head (turn_left directions)))
else if first_move == rght then
execute_orders x y rest lang (fromJust (head (turn_right directions)))
else
Debug.todo "branch '_' not implemented"
我需要 x 和 y 为 Int,但从 String 转换为 Maybe Integer 类型...
无法将 String
简单地“转换”为 Int
,因为在那个方向上没有完全映射。例如,字符串 "banana"
没有明显的整数表示。你挣扎的不是“类型”,而是不同的事物因不同的原因而不同,并且必须做出巩固这些差异的决定。
String.toInt
returns a Maybe Int
因为当给定字符串没有合理且明显的整数表示时,由您决定要做什么。您可以选择只默认为 0
、-1
,还是显示字符串 -
。这些都是针对不同场景的合理选择,但由于 String.toInt
不可能知道你处于哪种场景,你 必须做出决定。
如果您只是想回退到默认整数,或者您确定任何给定的字符串都将具有有效的整数表示,您可以使用 Maybe.withDefault
:
String.toInt "42" |> Maybe.withDefault 0 -- 42
String.toInt "banana" |> Maybe.withDefault 0 -- 0
或者您可以使用 case
表达式来做一些完全不同的事情:
case String.toInt s of
Just n ->
span [] [ text (n + 1 |> String.fromInt) ]
Nothing ->
button [ onClick ... ] [ text "-" ]
由于 HTML 输入事件总是处理字符串,您需要在某个时候解析您收到的字符串。我看到了三个选项。
首先,您的事件消息的有效负载可以是 String
,然后您在 update
函数中解析该字符串。
type Msg
= SetX String
type alias Model =
{ x : Int }
update msg model =
case msg of
SetX xString ->
case String.toInt xString of
Nothing ->
model
Just x ->
{ model | x = x }
上面的一个变体是将 x
作为 Maybe String
存储在你的模型上,如果你愿意的话,and/or 存储一些用于跟踪错误状态的值在发送无效值时显示消息。这是最简单和最灵活的方法。
第二个选项是将事件消息的有效负载设为 Maybe Int
。 Html.Events.onInput
是一个接受“标记器”函数的函数,该函数接受 String
并输出一条消息。虽然您可以只提供一个接受 String
有效负载的消息构造函数,但您也可以提供一个函数,该函数在创建 Msg
值之前进行一些处理。
type Msg
= SetY (Maybe Int)
view model =
input [ type_ "number", onInput (\value -> String.toInt value |> SetY) ] []
第三个选项是将事件消息的有效负载设为 Int
。这将要求您使用 Html.Events.on
创建自己的 onInput
版本。 on
使用 Json.Decode.Decoder msg
而不是 onInput
使用的 (String -> msg)
函数。仅当解码器成功时才会生成消息;也就是说,产生解码器失败的事件将被忽略。这允许您处理事件处理程序中解析失败的情况。
type Msg
= SetY Int
view model =
input [ type_ "number", onNumericInput SetY ] []
-- based on https://github.com/elm/html/blob/97f28cb847d816a6684bca3eba21e7dbd705ec4c/src/Html/Events.elm#L115-L122
onNumericInput : (Int -> msg) -> Attribute msg
onNumericInput toMsg =
let
alwaysStop : a -> ( a, Bool )
alwaysStop x =
( x, True )
failIfNothing : Maybe a -> Decode.Decoder a
failIfNothing maybe =
case maybe of
Nothing ->
Decode.fail "Parsing failed"
Just a ->
Decode.succeed a
decoder : Decode.Decoder msg
decoder =
Events.targetValue
|> Decode.map String.toInt
|> Decode.andThen failIfNothing
|> Decode.map toMsg
in
Events.stopPropagationOn "input" (decoder |> Decode.map alwaysStop)
查看完整示例中的最后两个选项
我是 Elm 的完全初学者,我正在为类型而苦苦挣扎。我一直在与类型作斗争,但我找不到解决此问题的便捷方法。有问题的代码是这样的:
view model =
div []
[ h1 [] [ text "Robot" ]
, input [ onInput SetX, value model.x ] []
, input [ onInput SetY, value model.y ] []
, input [ type_ "Int" , onInput SetCommands, value model.x] []
, button [ onClick ButtonPressed] []
, input [ readonly True ] []
]
X 和 Y 是在以下函数中使用的整数:
execute_orders : Int -> Int -> String -> String -> String -> { x : Int, y : Int, dir : String }
execute_orders x y commands lang dir =
let
moves =
case lang of
"English" ->
"RLF"
"French" ->
"HVG"
"Swedish" ->
"DGT"
_ ->
Debug.todo "No Language Found"
fw =
right 1 moves
directions =
facing dir
rght =
left 1 moves
lft =
slice 1 2 moves
first_move =
left 1 (toUpper commands)
rest =
slice 1 (length commands) (toUpper commands)
in
if first_move == "" then
{ x = x, y = y, dir = fromJust (head directions) }
else if first_move == fw then
if dir == "N" then
execute_orders x (y - 1) rest lang dir
else if dir == "E" then
execute_orders (x + 1) y rest lang dir
else if dir == "S" then
execute_orders x (y + 1) rest lang dir
else
execute_orders (x - 1) y rest lang dir
else if first_move == lft then
execute_orders x y rest lang (fromJust (head (turn_left directions)))
else if first_move == rght then
execute_orders x y rest lang (fromJust (head (turn_right directions)))
else
Debug.todo "branch '_' not implemented"
我需要 x 和 y 为 Int,但从 String 转换为 Maybe Integer 类型...
无法将 String
简单地“转换”为 Int
,因为在那个方向上没有完全映射。例如,字符串 "banana"
没有明显的整数表示。你挣扎的不是“类型”,而是不同的事物因不同的原因而不同,并且必须做出巩固这些差异的决定。
String.toInt
returns a Maybe Int
因为当给定字符串没有合理且明显的整数表示时,由您决定要做什么。您可以选择只默认为 0
、-1
,还是显示字符串 -
。这些都是针对不同场景的合理选择,但由于 String.toInt
不可能知道你处于哪种场景,你 必须做出决定。
如果您只是想回退到默认整数,或者您确定任何给定的字符串都将具有有效的整数表示,您可以使用 Maybe.withDefault
:
String.toInt "42" |> Maybe.withDefault 0 -- 42
String.toInt "banana" |> Maybe.withDefault 0 -- 0
或者您可以使用 case
表达式来做一些完全不同的事情:
case String.toInt s of
Just n ->
span [] [ text (n + 1 |> String.fromInt) ]
Nothing ->
button [ onClick ... ] [ text "-" ]
由于 HTML 输入事件总是处理字符串,您需要在某个时候解析您收到的字符串。我看到了三个选项。
首先,您的事件消息的有效负载可以是 String
,然后您在 update
函数中解析该字符串。
type Msg
= SetX String
type alias Model =
{ x : Int }
update msg model =
case msg of
SetX xString ->
case String.toInt xString of
Nothing ->
model
Just x ->
{ model | x = x }
上面的一个变体是将 x
作为 Maybe String
存储在你的模型上,如果你愿意的话,and/or 存储一些用于跟踪错误状态的值在发送无效值时显示消息。这是最简单和最灵活的方法。
第二个选项是将事件消息的有效负载设为 Maybe Int
。 Html.Events.onInput
是一个接受“标记器”函数的函数,该函数接受 String
并输出一条消息。虽然您可以只提供一个接受 String
有效负载的消息构造函数,但您也可以提供一个函数,该函数在创建 Msg
值之前进行一些处理。
type Msg
= SetY (Maybe Int)
view model =
input [ type_ "number", onInput (\value -> String.toInt value |> SetY) ] []
第三个选项是将事件消息的有效负载设为 Int
。这将要求您使用 Html.Events.on
创建自己的 onInput
版本。 on
使用 Json.Decode.Decoder msg
而不是 onInput
使用的 (String -> msg)
函数。仅当解码器成功时才会生成消息;也就是说,产生解码器失败的事件将被忽略。这允许您处理事件处理程序中解析失败的情况。
type Msg
= SetY Int
view model =
input [ type_ "number", onNumericInput SetY ] []
-- based on https://github.com/elm/html/blob/97f28cb847d816a6684bca3eba21e7dbd705ec4c/src/Html/Events.elm#L115-L122
onNumericInput : (Int -> msg) -> Attribute msg
onNumericInput toMsg =
let
alwaysStop : a -> ( a, Bool )
alwaysStop x =
( x, True )
failIfNothing : Maybe a -> Decode.Decoder a
failIfNothing maybe =
case maybe of
Nothing ->
Decode.fail "Parsing failed"
Just a ->
Decode.succeed a
decoder : Decode.Decoder msg
decoder =
Events.targetValue
|> Decode.map String.toInt
|> Decode.andThen failIfNothing
|> Decode.map toMsg
in
Events.stopPropagationOn "input" (decoder |> Decode.map alwaysStop)
查看完整示例中的最后两个选项