haskell-jp / beginners #16 at 2021-06-01 18:49:48 +0900

はじめまして。
Haskell学習のため [Advent of Code 2020]() をちまちま解いているところなのですが、解答できたけれどコードが汚くなってしまうものがあります。
https://adventofcode.com/2020/day/8 8日めのこの問題、こういうコードになりました。
https://github.com/kyoheiu/aoc2020-haskell/blob/master/day08/app/Main.hs

そもそも最適なアプローチはこれではないということかもしれませんが、それはいったんおいて、このアプローチでもっとHaskellっぽいコードにリファクタリングするにはどういう考え方があるでしょうか?
とりあえず引数の数が多いのが嫌な感じなのですが…。
@kyoheiu この中でいうと、例えば「符号付き整数文字列を整数に変換する」関数を1つにまとめると、ちょっと簡潔になる気がします。持たなくてはならない状態量が多いので、引数の数が多いのはどうにもならない気がしますね……。
素朴かつ関数的に(ここでは、do構文を使わない、配列を使わない、程度の意味)考えるなら、
性能はともかく、こんな感じでしょうか。(詳細は書いてないので動かしてませんが。^^;)

module Main where

import Data.Char ( toUpper )

main :: IO ()
main = print . lastState . run . load . map toUpper . filter ('+' /=)
     =<< readFile "day08.txt"

type Code = (OP, Arg)
data OP
    = NOP
    | ACC
    | JMP
    deriving (Eq, Show, Read)

type Arg = Int

type Offset = Int

type Mem = ([Code], Code, [Code])
derefPC :: Mem -> Code
derefPC (_, c, _) = c

jump :: Offset -> Mem -> Mem
jump offset mem = undefined 

type Acc = Int
updAcc :: Offset -> Acc -> Acc
updAcc offset acc = acc + offset

type Count = Int
updCount :: Count -> Count
updCount count = succ count 

type Output = String

type VMState = (Mem, Acc, Count, Output)

isFinalState :: VMState -> Bool
isFinalState vm = undefined 

load :: String -> VMState
load src = case map toCode (lines src) of
    h : t -> (([], h, t), 0, 0, "")

toCode :: String -> Code
toCode ln = case words ln of
    op : num : _ -> (read op, read num)

run :: VMState -> [VMState]
run st = st : if isFinalState st then [] else run (step st)

step :: VMState -> VMState
step vm = execute (decode (fetch vm)) vm

fetch :: VMState -> Code
fetch (mem, _, _, _) = derefPC mem

decode :: Code -> (VMState -> VMState)
decode code vm = case code of
    (op, offset) -> undefined

execute :: (VMState -> VMState) -> VMState -> VMState
execute = id

lastState :: [VMState] -> (Acc, Count, Output)
lastState states = case last states of
    (_,acc,cnt,msg) ->  (acc, cnt, msg)

命令列をロードしたメモリをいわゆるZipperにしてあるので、jump も簡単に実装できるでしょう。
私ならStateモナドを使いますね(アプローチが違ってそうですが…)
「このアプローチで」縛りがなくなり始めてるので私も自分が書いてみたコードを貼り付けてみます。Haskell的かどうかはわかりませんが見た目はスッキリしているはずです。
皆さんありがとうございます。コード勉強になります!精読させていただきます。
@ Stateモナドですよね、作り終わってから気が付きました。慣れていないので勉強がてら使ってみます。
https://github.com/graninas/The-Voids-Of-Haskell#Clean-Functional-Code こういうの欲しい…