趣旨
友達とボードゲームバーで遊んだDomemoが面白すぎたので
勉強も兼ねて多言語でプログラムしてみよう
Domemoについてはこちらを
ドメモ(Domemo)とかいうイタリアの伝説の確率ゲームが面白い。(ちもろぐ)
成果物
ソースコードはBitBucketに入れてますので、MITライセンスの元でお好きにしてください!
※公開リポジトリだけど、みんな好き勝手に触れるのかよくわかってない...
git clone https://aoi_erimiya@bitbucket.org/aoi_erimiya/domemo.git
何の言語でやろうか?
折角やるなら、聞いた事あるけど触ったことないモダンな言語でやってみよう!
→Python, Ruby, Rust, F#, Nim, D, Go, Swift, Kotlin
※Python, Rubyは経験あるけどリファレンスとして実装
かかった時間
F# > Rust > D > Go > Nim > Swift == Kotlin > Ruby > Python
パラダイム違ったり、ドキュメント見つけにくいやつはやっぱり時間かかるね。
各言語の感想
F#-たのしい
関数型とオブジェクト指向のマルチパラダイム。
オブジェクト指向で書いたらC#っぽくなるし
関数型で書いたらOcamlっぽくなる。
いいところ
- 軽量構文で書いてるとコードがきれいに揃って美しい(|>を使いたい)
- .NetFrameworkで動く(windowsならVisualStudioいれてたら気軽に使える!)
- どうやったらF#っぽいコードになるのか考えるのが楽しい
詰まったところ
- リストが不変なので、オブジェクト指向や命令型言語とは実装方法が大きく異なる
多言語:リストを作る→シャッフルする
F#:はじめからシャッフルされた状態のリストを作る
let rand = Random()
let mutable cards = [1;2;3;4;5;6;7]
while cards.Length < 28 do
let randCard = rand.Next(1, 8)
List.countBy (fun elem -> if (elem = randCard) then 1 else 0) cards
|> List.filter (fun tpl -> fst(tpl) = 1 && snd(tpl) < randCard)
|> List.iter (fun x -> cards <- List.append [randCard] cards)
|> ignore
- 素直に書いたらほぼC#と同じようなコードになっちゃう
- 公式ドキュメントの機械翻訳がめちゃくちゃでいまいちわかりにくい…
Rust-賢い
関数型っぽいC++。
いいところ
- 所有権とかの考え方が秀逸で、ポインタを安全に使える
- コンパイラが非常に賢い
- 関数型譲りのmatchが強力
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
let guess: i32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
詰まったところ
- 配列をうまくスライスしたかったけど、まだ理解できてない...
- indexOf()がないのでbinary_search()からのunwrap()
fn match_card(&mut self, guess_card: i32) -> i32 {
let mut ret = 0;
if self.cards.contains(&guess_card) == true {
println!("jackpot.");
let result = self.cards.binary_search(&guess_card);
let card_index: usize = result.unwrap();
ret = self.cards.remove(card_index);
} else {
println!("miss.");
}
ret
}
D-安心
めっちゃ書きやすいC++。
いいところ
-
C++譲りのコードの見やすさ ※たぶんCライク畑で育ったから
-
無駄がなく簡潔に書けるforeach
foreach (Player player; players)
{
player.show();
}
- ,区切りで書けてわざわざ文字列結合する必要がない標準出力
writeln(player.getName(), " is called", guessCard);
詰まったところ
- コレクション型のindexOf()がないところ
- 数値->文字列変換にformattedWrite()使わないといけない
※普通にキャストやろうとして苦労した
foreach (idx; 0 .. 4)
{
auto writer = appender!string();
formattedWrite(writer, "%s%d", "Player", idx + 1);
players[idx] = new Player(writer.data, cards[idx * 5 .. (idx + 1) * 5]);
players[idx].show();
}
Nim-エレガント
コンパイル言語なのにスクリプト言語のように書ける。
いいところ
- pythonっぽく書ける
- ちょっと関数型っぽい
- rubyのように気の利いたユーティリティ関数
- 第一引数のメンバのように呼び出せる機構
type
Player = ref object of RootObj
name*: string
cards : seq[int]
proc check(player:Player): bool = return player.cards.len == 0
# Player型の変数playerがいるとして、こんな感じ
player.check()
つまったところ
- seqとかopenArrayとか便利だけど使い分けが細かい
- 配列掛け算ない?なんかcycle()ってあるみたいやね?うわぁ...
var cards:seq[int] =
concat(@[1], @[2].cycle(2), @[3].cycle(3), @[4].cycle(4),
@[5].cycle(5),@[6].cycle(6),@[7].cycle(7))
Kotlin-スマート
Javaの大変だったところがめっちゃええ感じに!
いいところ
- ごてごて書かなくていい
- TypeScriptとJavaのあいの子みたいな文法
- スクリプト言語のような気軽さ
つまったところ
- MutableListをsorted()したりslice()するとListが返ってきて型エラー→toMutableList()の乱舞…
val cards = mutableListOf(1,2,2,3,3,3,4,4,4,4,5,5,5,5,5,6,6,6,6,6,6,7,7,7,7,7,7,7)
cards.shuffle()
val sliceCards = cards.slice(cards.count()-4..cards.count()-1).sorted()
var openCards: MutableList<Int> = sliceCards.toMutableList()
val players: MutableList<Player> = mutableListOf()
for(i in 0 until 4){
var playerCards = cards.slice(i*5..(i+1)*5-1).sorted()
players.add(Player("Player"+(i+1), playerCards.toMutableList()))
}
他の言語たち
残りの言語はDomemo実装によるプログラミング言語比較 その2にて!
ご意見など
各言語の専門家の方、もっと綺麗に書けるよとかご意見ありましたら
スキル磨きたいのでぜひ教えてください!!