前回の出題にある通りです。
あらためて書くと、標準入力から与えられる文字列のなかに文字@が何回現れるかを数えてその個数を標準出力せよ、という問題でした。
この問題定義はあいまいで、文字列中に @ がなかった場合について触れていません。 パターンマッチの実行時エラーを許すことにより、短いプログラムを書くことができるかもしれません。 が、これは禁じ手とします。@ がなかったときは 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 :: (String -> String) -> IO () は、今回のような標準入力から標準出力へ渡すときに用いるうってつけの方法です。 上記のプログラムを interact を用いて書き換えると次のようになります:
main = interact $ \xs -> show $ length [x | x <- xs, x == '@']
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 とリスト内包表記を使えばもしかすると?(外しているかも)
ゴルフは奇妙な競技で、実務ではこういうことはやりません。 しかし、
- 同じことをするための異なるプログラムについて思いを馳せる
- 文法やライブラリをくまなく探す
- 他人と競い合う
などの効果があります。これは良いと思います。
cojna さんから 37 bytes 解を教わりました、ありがとうございます。
http://lingr.com/room/sugoih/archives/2013/05/09#message-15214589
https://gist.github.com/cojna/5547175
main=interact(\x->show$sum[1|'@'<-x])