Skip to content

Instantly share code, notes, and snippets.

@ikedaisuke
Created May 9, 2013 07:20
Show Gist options
  • Save ikedaisuke/5546070 to your computer and use it in GitHub Desktop.
Save ikedaisuke/5546070 to your computer and use it in GitHub Desktop.

Haskell golf @ すごいHaskell読書会in大阪 8th meeting

2013-05-09

@ikegami__

問題

前回の出題にある通りです。

あらためて書くと、標準入力から与えられる文字列のなかに文字@が何回現れるかを数えてその個数を標準出力せよ、という問題でした。

不備

この問題定義はあいまいで、文字列中に @ がなかった場合について触れていません。 パターンマッチの実行時エラーを許すことにより、短いプログラムを書くことができるかもしれません。 が、これは禁じ手とします。@ がなかったときは 0 を出力してください。

この不備をついてさらに短くできるようであれば、見てみたいとは思います。

出力について、数字の後ろに改行文字を置く必要はありません。

サンプル

サンプル入力:

ACTIVE
@iseebi
@yashigani
INACTIVE
@ikegami__

サンプル出力:

3

サンプル入力:

Hello, world!

サンプル出力:

0

では、プログラムの長さを縮めていきましょう。ゴルフは最短のコードを作るゲームです。

素朴に考える

main = do
  xs <- getContents
  print $ length [x | x <- xs, x == '@']

標準入力から文字列をとってくる方法の一つは getContents :: IO String です。 標準出力は putStr :: String -> IO () と print :: Show a => a -> IO () の二つが思いつきます。

putStr . show と print は同じプログラムですが、より短いほうを選びました。

二項演算 ($) :: (a -> b) -> a -> b は括弧を省略したいときに使う記法です。

文字列から文字 '@' を抽出し、個数を数える方法はいくつもあります。 ここで挙げたのは、リストの内包表記を利用したものです。

interact を用いる

interact :: (String -> String) -> IO () は、今回のような標準入力から標準出力へ渡すときに用いるうってつけの方法です。 上記のプログラムを interact を用いて書き換えると次のようになります:

main = interact $ \xs -> show $ length [x | x <- xs, x == '@']

filter を用いる

filter :: (a -> Bool) -> [a] -> [a] を用いて、リスト内包表記を書き直します:

main = interact $ show . length . filter (=='@')

空白を詰めて整形する

1 byte も見逃さずに削ります:

-- ruler
--       1         2         3         4
--34567890123456789012345678901234567890
main=interact$show.length.filter(=='@')

39 bytes になりました。

真の解

37 bytes でできるらしいです。達成した参加者の方に説明を任せます。 length.filter の代わりに sum とリスト内包表記を使えばもしかすると?(外しているかも)

ゴルフの教育的な一面

ゴルフは奇妙な競技で、実務ではこういうことはやりません。 しかし、

  • 同じことをするための異なるプログラムについて思いを馳せる
  • 文法やライブラリをくまなく探す
  • 他人と競い合う

などの効果があります。これは良いと思います。

@ikedaisuke
Copy link
Author

cojna さんから 37 bytes 解を教わりました、ありがとうございます。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment