エンジニアのソフトウェア的愛情

または私は如何にして心配するのを止めてプログラムを・愛する・ようになったか

「どう書く?」とつねに問う姿勢が大事、だと、思う。

こういう面白そうな話題は、まぜてくださいー。
あ、残念ながらオフラインのほうの参加はちょっと無理。ほんと残念ながら。


先に結果。

以下、自分のコードの解説。

C++

わたしも最初struct Card { char suit; int rank; };と書き始めたのですが、虎塚さんのエントリの最後の方に、Rubyで3行、と書かれていたのを見て、これは手続き的に書いたんじゃだめだ、と方向転換。
例題をよく読むと、スートは役に関係していないことがわかり、ということはスートの文字を区切り文字にして文字列分割すればいいというところまでは思いついたものの。C++にそんなのないので、for_eachテンプレートでスートの文字を空白文字に置き換えて、文字列ストリームで読み込むというリソース食いまくりの暴挙に出てみました。
最後はtransformテンプレートで文字列につっこんで文字列比較に持ち込んでいます。

#include <iostream>
#include <sstream>
#include <string>
#include <map>
#include <algorithm>

void removeSuit(std::string::value_type& c)
{
    switch(c)
    {
    case 'S':
    case 'H':
    case 'D':
    case 'C':
        c = ' ';
    }
}

int second(const std::map<std::string, int>::value_type& value)
{
    return value.second + '0';
}

std::string poker(const std::string& cards)
{
    std::string s = cards;
    std::for_each(s.begin(), s.end(), removeSuit);
    std::istringstream ss(s);

    std::string                n;
    std::map<std::string, int> m;
    while(ss >> n)
    {
        ++m[n];
    }

    std::string t(m.size(), ' ');
    std::transform(m.begin(), m.end(), t.begin(), second);
    std::sort(t.begin(), t.end());

    if(t == "1112") { return "1P"; }
    if(t == "113")  { return "3K"; }
    if(t == "122")  { return "2P"; }
    if(t == "14")   { return "4K"; }
    if(t == "23")   { return "FH"; }
    return "--";
}

Haskell

宣言的なら関数型言語の出番とHaskell
ベタに書き下してみた。

import List

parse []             rs = sort $ map length $ group $ sort rs
parse (_:'1':'0':ss) rs = parse ss ('p':rs)
parse (_:d:ss)       rs = parse ss (d:rs)

poker cs = case parse cs [] of
  [1,1,1,1,1] -> "--"
  [1,1,1,2]   -> "1P"
  [1,2,2]     -> "2P"
  [1,1,3]     -> "3K"
  [2,3]       -> "FH"
  [1,4]       -> "4K"

Haskell、もう一度

Haskellで書き下しみて最初に思ったのが。なんか野暮ったい。

  1. 文字列から同じランクのカードの数のリストを作るもっとストレートな方法がないのか?
  2. HaskellってMap(キー・バリューのセットを格納するコンテナ)ないんだっけ?

まずMapのほう。過去に使ったことがなかっただけでちゃんとありました。これでカードの枚数のリストから役への対応付けができます。

次に枚数のリストを作る段ですが。words関数を使えば分割できるなー、と気がつき、ぢゃあスートの文字を空白に置き換えてwords関数を適用すればよい、となり、置き換えの関数ないけど畳み込み関数使えばどうにかなるだろう、ということで。
結果、こんな感じになりました。

import List(group, sort)
import Data.Map(fromList, (!))

parse = sort.(map length).group.sort.words.(foldr (\x xs -> (if elem x "SHDC" then ' ' else x):xs) [])

poker cs = fromList [([1,1,1,1,1], "--"), ([1,1,1,2], "1P"), ([1,2,2], "2P"), ([1,1,3], "3K"), ([2,3], "FH"), ([1,4], "4K")] ! (parse cs)

まだしっくりこない

もう一段、なんかできそうな気がするんだけど、現時点では思いつかない。