Skip to content

Instantly share code, notes, and snippets.

@PhilOwen
Created January 7, 2018 07:08
Show Gist options
  • Save PhilOwen/88671dfa2e645fe9e7763a2647b0f934 to your computer and use it in GitHub Desktop.
Save PhilOwen/88671dfa2e645fe9e7763a2647b0f934 to your computer and use it in GitHub Desktop.
HaskellのArrowで、実際のプログラミング問題を解く

アローを使ってプログラミングの問題を解いてみる。
HackerRankで、3つの積み木があり、 高さが揃うまで積み木を上から取っていけ、という問題に使った。

アローというのは、モナドやカテゴリと同じく、数学的な概念。 モナドが説明しにくいように、アローも抽象的で何ともわかりにくい。
モナドほど色々なことができないということになっているが、 試してみるともわりと出番は多かった。 制約があるのに、問題なく何か機能を実装できるなら、 そのほうが安全で、スパゲッティにもなりにくいはずである。

プログラム的には、アローの見た目通り、関数をつなげていく。 関数に引数を明示的に与えない、ポイントフリースタイルがメインになる。 >>>やら|||やら、記号が呪文ぽい感じだが、 やりようによっては簡潔になる。 一時期流行った。
少なくとも、>>>は関数合成の.逆版で、普通に有用。

参考

name: arrow-test
version: 0.1.0.0
build-type: Simple
cabal-version: >=1.10
executable arrow-test
main-is: Main.hs
ghc-options: -threaded -rtsopts -with-rtsopts=-N
build-depends: base
, bytestring
default-language: Haskell2010
import Control.Arrow
import qualified Data.ByteString.Char8 as B
type Stack = ([Int], Int)
height :: Stack -> Int
height = snd
pop :: Stack -> Stack
pop (x:xs, h) = (xs, h-x)
go :: (Stack, Stack, Stack) -> Int
go = removeElem >>> (id ||| go)
removeElem :: (Stack, Stack, Stack) -> Either Int (Stack, Stack, Stack)
removeElem (s1, s2, s3) =
let hs = height <$> [s1, s2, s3]
m = maximum hs
in if all (==m) hs
then Left m
else
case hs of
h:_ | h == m -> Right (pop s1, s2, s3)
_:h:_ | h == m -> Right ( s1, pop s2, s3)
_:_:h:_ | h == m -> Right ( s1, s2, pop s3)
main :: IO ()
main = B.interact $ \cs -> do
let l1:ls = B.lines cs
ns = readInt <$> B.words l1
s1:s2:s3:_ = [ (s, sum s) | (n, l)<-zip ns ls, let s = map readInt $ take n $ B.words l]
let sameHeight = go (s1, s2, s3)
B.pack $ show sameHeight
readInt :: B.ByteString -> Int
readInt bs = n where
Just (n, _) = B.readInt bs
resolver: lts-10.2
packages:
- '.'
extra-deps: []
flags: {}
extra-package-dbs: []
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment