画面にこう出れば正解
どの言語で書いても、同じ入力なら画面に出る文字は1文字も違わない、というのが正解の決め方です(例は Python で動かしたときのコマンドです)。
日本語の単語
$ python3 wordcount.py りんご みかん りんご
りんご: 2
みかん: 1英字
$ python3 wordcount.py b a b c a b
b: 3
a: 2
c: 1数字の言葉
$ python3 wordcount.py 2 10 2 1
2: 2
10: 1
1: 11つだけ
$ python3 wordcount.py x
x: 1何もない
$ python3 wordcount.py
単語がありません大文字と小文字は別の単語
$ python3 wordcount.py Go go GO go
Go: 1
go: 2
GO: 110言語のプログラム
どれもそのまま保存して動かせます。毎朝、上の正解の例をすべて実際に動かして確かめています。
wordcount.php8.4.26 で 6 / 6 件の出力が正解と一致
<?php
// 受け取った単語がそれぞれ何回出てきたかを、はじめて出てきた順に出す
$words = array_slice($argv, 1);
if (count($words) === 0) {
echo "単語がありません\n";
exit;
}
// 連想配列は入れた順を保つが、"10" のようなキーは整数の 10 に変わる。順番は別の配列 $order に記録する
$counts = [];
$order = []; // はじめて出てきた順
foreach ($words as $w) {
if (!isset($counts[$w])) {
$order[] = $w;
$counts[$w] = 0;
}
$counts[$w]++;
}
foreach ($order as $w) {
echo $w . ": " . $counts[$w] . "\n";
}
動かし方(ファイルを保存したフォルダで) ・ PHP を入れる
$ php wordcount.php りんご みかん りんご
wordcount.py3.14.7 で 6 / 6 件の出力が正解と一致
# 受け取った単語がそれぞれ何回出てきたかを、はじめて出てきた順に出す
import sys
words = sys.argv[1:]
if not words:
print("単語がありません")
sys.exit()
counts = {}
order = [] # はじめて出てきた順
for w in words:
if w not in counts:
order.append(w)
counts[w] = 0
counts[w] += 1
# Python 3.7 からの dict は入れた順を保つが、ほかの言語でも同じ考え方で書けるように、順番は order から取る
for w in order:
print(f"{w}: {counts[w]}")
動かし方(ファイルを保存したフォルダで) ・ Python を入れる
$ python3 wordcount.py りんご みかん りんご
wordcount.go1.27.2 で 6 / 6 件の出力が正解と一致
// 受け取った単語がそれぞれ何回出てきたかを、はじめて出てきた順に出す
package main
import (
"fmt"
"os"
)
func main() {
words := os.Args[1:]
if len(words) == 0 {
fmt.Println("単語がありません")
return
}
// Go の map は range で回す順番が決まっておらず、動かすたびに変わる。順番は別のスライス order に記録する
counts := map[string]int{}
order := []string{} // はじめて出てきた順
for _, w := range words {
if _, ok := counts[w]; !ok {
order = append(order, w)
}
counts[w]++
}
for _, w := range order {
fmt.Printf("%s: %d\n", w, counts[w])
}
}
動かし方(ファイルを保存したフォルダで) ・ Go を入れる
$ go run wordcount.go りんご みかん りんご
wordcount.js24.15.0 で 6 / 6 件の出力が正解と一致
// 受け取った単語がそれぞれ何回出てきたかを、はじめて出てきた順に出す
const words = process.argv.slice(2);
if (words.length === 0) {
console.log("単語がありません");
process.exit();
}
// オブジェクト {} は「2」「10」のような数字に見えるキーを小さい順で先に並べるので、入れた順を保つ Map を使う
const counts = new Map();
const order = []; // はじめて出てきた順
for (const w of words) {
if (!counts.has(w)) {
order.push(w);
counts.set(w, 0);
}
counts.set(w, counts.get(w) + 1);
}
for (const w of order) {
console.log(`${w}: ${counts.get(w)}`);
}
動かし方(ファイルを保存したフォルダで) ・ JavaScript を入れる
$ node wordcount.js りんご みかん りんご
WordCount.java21.0.12 で 6 / 6 件の出力が正解と一致
// 受け取った単語がそれぞれ何回出てきたかを、はじめて出てきた順に出す
import java.util.LinkedHashMap;
import java.util.Map;
public class WordCount {
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("単語がありません");
return;
}
// HashMap は取り出す順番が決まっていない。LinkedHashMap は入れた順に取り出せる
Map<String, Integer> counts = new LinkedHashMap<>();
for (String w : args) {
counts.put(w, counts.getOrDefault(w, 0) + 1);
}
for (Map.Entry<String, Integer> e : counts.entrySet()) {
System.out.println(e.getKey() + ": " + e.getValue());
}
}
}
動かし方(ファイルを保存したフォルダで) ・ Java を入れる
$ java WordCount.java りんご みかん りんご
wordcount.cs10.0.402 で 6 / 6 件の出力が正解と一致
// 受け取った単語がそれぞれ何回出てきたかを、はじめて出てきた順に出す
if (args.Length == 0)
{
Console.WriteLine("単語がありません");
return;
}
var counts = new Dictionary<string, int>();
// Dictionary を回したときの順番は決まりになっていないので、はじめて出てきた順を別のリストに記録する
var order = new List<string>();
foreach (string w in args)
{
if (!counts.ContainsKey(w))
{
counts[w] = 0;
order.Add(w);
}
counts[w]++;
}
foreach (string w in order)
{
Console.WriteLine($"{w}: {counts[w]}");
}
動かし方(ファイルを保存したフォルダで) ・ C# を入れる
$ dotnet run wordcount.cs -- りんご みかん りんご
wordcount.rs1.99.0 で 6 / 6 件の出力が正解と一致
// 受け取った単語がそれぞれ何回出てきたかを、はじめて出てきた順に出す
use std::collections::HashMap;
fn main() {
let args: Vec<String> = std::env::args().skip(1).collect();
if args.is_empty() {
println!("単語がありません");
return;
}
let mut counts: HashMap<String, i32> = HashMap::new();
// HashMap を回す順番は決まっておらず、動かすたびに変わるので、はじめて出てきた順を別の Vec に記録する
let mut order: Vec<String> = Vec::new();
for w in &args {
if !counts.contains_key(w) {
order.push(w.clone());
}
*counts.entry(w.clone()).or_insert(0) += 1;
}
for w in &order {
println!("{}: {}", w, counts[w]);
}
}
動かし方(ファイルを保存したフォルダで) ・ Rust を入れる
$ rustc wordcount.rs && ./wordcount りんご みかん りんご
wordcount.rb4.0.7 で 6 / 6 件の出力が正解と一致
# 受け取った単語がそれぞれ何回出てきたかを、はじめて出てきた順に出す
words = ARGV
if words.empty?
puts "単語がありません"
exit
end
counts = Hash.new(0) # まだ無いキーを引くと 0 が返る
order = [] # はじめて出てきた順
words.each do |w|
order << w unless counts.key?(w)
counts[w] += 1
end
# Ruby の Hash は入れた順を保つが、ほかの言語でも同じ考え方で書けるように、順番は order から取る
order.each do |w|
puts "#{w}: #{counts[w]}"
end
動かし方(ファイルを保存したフォルダで) ・ Ruby を入れる
$ ruby wordcount.rb りんご みかん りんご
wordcount.kt2.4.21 で 6 / 6 件の出力が正解と一致
// 受け取った単語がそれぞれ何回出てきたかを、はじめて出てきた順に出す
fun main(args: Array<String>) {
if (args.isEmpty()) {
println("単語がありません")
return
}
// HashMap は取り出す順番が決まっていない。LinkedHashMap は入れた順に取り出せる
// (mutableMapOf() の中身も LinkedHashMap だが、順番が大事なことを型の名前で示す)
val counts = LinkedHashMap<String, Int>()
for (w in args) {
counts[w] = counts.getOrDefault(w, 0) + 1
}
for ((w, n) in counts) {
println("$w: $n")
}
}
動かし方(ファイルを保存したフォルダで) ・ Kotlin を入れる
$ kotlinc wordcount.kt -include-runtime -d wordcount.jar && java -jar wordcount.jar りんご みかん りんご
wordcount.swift6.3.3 で 6 / 6 件の出力が正解と一致
// 受け取った単語がそれぞれ何回出てきたかを、はじめて出てきた順に出す
let args = CommandLine.arguments.dropFirst()
if args.isEmpty {
print("単語がありません")
} else {
var counts: [String: Int] = [:]
// Dictionary を回す順番は決まっておらず、動かすたびに変わるので、はじめて出てきた順を別の配列に記録する
var order: [String] = []
for w in args {
if counts[w] == nil {
order.append(w)
}
counts[w, default: 0] += 1
}
for w in order {
print("\(w): \(counts[w]!)")
}
}
動かし方(ファイルを保存したフォルダで) ・ Swift を入れる
$ swift wordcount.swift りんご みかん りんご
言語でつまずきやすいところ
初心者がよく書く形(各言語で最初に習う書き方をそのまま使ったもの)を、同じ正解表で動かした結果です。言語を出した答えごとにまとめ、赤い字はその言語が実際に出した間違った行です。
| 入力 | 初心者がよく書く形の答え(10言語) |
|---|---|
日本語の単語りんご みかん りんご |
|
英字b a b c a b |
|
数字の言葉2 10 2 1 |
|
1つだけx | ✓10言語すべて正解 |
| 何もない | ✓10言語すべて正解 |
大文字と小文字は別の単語Go go GO go |
|
この回でやること
- 辞書(連想配列・map)に「単語 → 回数」を記録する
- はじめて出てきた順に、結果を出す
解説
辞書は「名前で引ける表」
辞書は、「りんご → 2」「みかん → 1」のように、名前(キー)で値を引ける入れ物です。言語によって dict(Python)、Map・オブジェクト(JavaScript)、map(Go)、HashMap・LinkedHashMap(Java・Kotlin)、Dictionary(C#)、Hash(Ruby)、連想配列(PHP)と呼び名が変わります。
取り出す順番は、言語ごとに決まりが違う
辞書から全部を取り出すとき、どの順番で出てくるかは言語によって違います。初心者がよく使う辞書で、実際に試した結果は次のとおりです。
| 辞書 | 取り出す順番 |
|---|---|
Python dict・Ruby Hash・PHP 連想配列・Kotlin mutableMapOf | 入れた順に出てくることが決まっている |
Go map・Rust HashMap・Swift Dictionary | 決まっておらず、動かすたびに変わる(同じ入力でも、通ったり落ちたりする) |
Java HashMap | 決まっていない。毎回同じ順に出るが、入れた順ではない(6件中4件) |
C# Dictionary | 決まっていない。今回の入力では入れた順に出た(6件中6件)が、要素を消したりすると崩れる |
JavaScript のオブジェクト {} | 文字のキーは入れた順だが、数字に見えるキー("2" や "10")だけ先に小さい順で並ぶ(6件中5件) |
「今回は通った」と「順番が決まっている」は別のことです。C# のように、たまたま入れた順に出ていても、言語の決まりとして約束されていなければ、頼らないほうが安全です。
この回の正解は「はじめて出てきた順」です。順番が決まっていない辞書を使うときは、出てきた順を別の並び(リスト・配列)に記録しておき、その順に取り出します。Java・Kotlin なら、入れた順を保つ LinkedHashMap を使う方法もあります。JavaScript なら、オブジェクトの代わりに Map を使うと入れた順に出てきます。
大文字と小文字
「Go」「go」「GO」は、別の単語として数えます。同じ単語として数えたいときは、先に小文字にそろえます。
練習
- 回数の多い順に並べ替えて出してみましょう
- 大文字と小文字を区別しないで数えてみましょう