LoginSignup
4
1

More than 3 years have passed since last update.

[Elm]カウンタープログラムを短く書きたい

Posted at

先日、このようなアプリを作りました。
kiji_stimer01.png
こちらです

スタート日時と、終了日時を設定するUI部分です。

書き始めてMsgが大量に必要になりそうだという事に気づきました。
日の設定にDownとUpの2つ、時の設定に4つ、分の設定に4つ。
それがスタートと終了の2つなので20個。

type Msg
    = DownStartDays
    | UpStartDays
    | DownEndDays
    | UpEndDays
    -- ....

書き始めて嫌になりました。

最初はたとえば「時」だけ作り始め、Msg,update,viewのどれも長くなりそうでなんとかしなければと思いました。

メッセージに値を含める

まずは、DownStartHours, DownStartHoursFiveなどとせずにメッセージに値を含めることにしました。

type Msg
    = DownStartHours Int
update msg model =
    case msg of
        DownStartHours step ->
            ( downHours step model.startEnd.start
                |> setStartPoint model
            , Cmd.none
            )
        DownEndHours step ->
            ( downHours step model.startEnd.end
                |> setStartPoint model
            , Cmd.none
            )
view model =
    div []
        [ button [ onClick (DownStartHours 1) ] [ text "⬇" ]
        , button [ onClick (DownStartHours 5) ] [ text "5⬇" ]
        ]

こんな感じです。

すぐに止め、StartかEndかも値で渡すようにしました。

type Msg
    = DownHours PointType  Int
type PointType
    = Start
    | End
update msg model =
    case msg of
        DownHours ptype step ->
            case ptype of
                Start ->
                    ( downHours step model.startEnd.start
                        |> setStartPoint model
                    , Cmd.none
                    )

                End ->
                    ( downHours step model.startEnd.end
                        |> setEndPoint model
                    , Cmd.none
                    )
view model =
    div []
        [ button [ onClick (DownHours Start 1) ] [ text "⬇" ]
        , button [ onClick (DownHours Start 5) ] [ text "5⬇" ]
        ]

あまり変わっていない感じがしますが、view部分は短くなりました。

view model =
    div []
        [ viewHours Start model.startEnd.start.hour
        , viewHours End model.startEnd.end.hour
        ]
viewHours : PointType -> Int -> Html Msg

このようにできるようになったので。

Down,UpからUpdateと抽象化してみた

最初からDown 5Up 5とせずに、Update 5Update -5とすればとは思っていました。
しかし、「時」のカウントアップでは23を超えたら0に、カウントダウンでは0を下回れば23にと愚直にしていたのが悩みどころでした。(これもなんとかしたい)

都合よく、ループする数字を作ってみました。
こちらです。

import Looper as Lp
updateHours : Int -> StartEnd.Point -> StartEnd.Point
updateHours amount point =
    { point | hour = Lp.new point.hour 0 23 |> Lp.add amount |> Lp.get }

update msg model =
    UpdateHours amount ptype ->
        case ptype of
            Start ->
                ( updateHours amount model.startEnd.start
                    |> setStartPoint model
                , Cmd.none
                )

            End ->
                ( updateHours amount model.startEnd.end
                    |> setEndPoint model
                , Cmd.none
                )

こんな感じになり、なかなか短くなったと思います。

modelを渡したい(けど)

update msg model =
    UpdateHours amount ptype ->
        case ptype of
            Start ->
                updateHours amount model.startEnd.start
            End ->
                updateHours amount model.startEnd.end
--
UpdateHours 1 Start
UpdateHours 1 End

このようにせず

update msg model =
    UpdateHours amount date ->
        updateHours amount date
--
UpdateHours 1 model.startEnd.start
UpdateHours 1 model.startEnd.end

このようにしてしまえば、と思いそうですが、これは危なそうです。
[Elm] Msg に更新後の値を含めるのは要注意

ところで

削除機能付きのTodoアプリみたいなものを作ったことがあるのですが、今思えばあれも少々危ういのかもしれません。
List.indexedMapをこんなふうに利用して削除機能を実現できるのか!、と思いましたが。

[0 1 2 3 4 5]
List.indexedMapを利用し3と4を削除する
---
> Del 3
> Del 4
update [0 1 2 3 4 5] =
    Del 3
update [0 1 2 4 5] =
    Del 4
=> [0 1 2 4]
---
[0 1 2 5]を望んていたはず。
---
> Del todos.id
これならよさそう

実際には高橋名人のような連打でもなんの問題もないのだろうと思います。

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1