give IT a try

プログラミング、リモートワーク、田舎暮らし、音楽、etc.

Qiitaに公開したRubyプログラムの紹介と、もし日本語でプログラミングできたら?の思考実験

お知らせ

Qiitaに「「Rubyで英語記事に含まれてる英単語を数えて出現数順にソートする」をカッコよく書いてみた」という記事を書きました。

前回のエントリ「シンプルでわかりやすいコードを書くためにあなたがすべきこと」の具体例を示したような記事になっているので、「よいコード」を書けるようになりたいプログラマの方はぜひ読んでみてください。

問題の内容と僕の書いたコード

ちなみに「Rubyで英語記事に含まれてる英単語を数えて出現数順にソートする」問題は、こんな問題でした。

以下のような英文が書かれているテキストファイルを読み込みます。

Interior design and decorating resource Houzz is the overall best Android app of the year, according to Google, which this evening announced the results of the first-ever Google Play Awards at its developer conference, Google I/O. While in previous years, the company had rounded up large numbers of apps for “Best of” lists, the new Google Play Awards instead follows Apple’s model of selecting winners across a variety of categories.
(以下略)

この中に含まれる英熟語(複合語)や単語を抽出し、次のように出現回数順に表示する、というのがこの問題のゴールです。

単語数(熟語以外):331
英熟語?------------------------------------------------------------------
2 Google I/O
2 Google Play Awards
1 And Google
1 Best App
1 Best Game
(略)
英単語------------------------------------------------------------------
22 the
11 and
11 of
8 a
6 apps
5 app
(略)

僕の書いたコード

僕はこんなコードを書きました。(Qiitaで公開したコードからところどころ変更しています)

class ExtractWord
  def self.execute(file_path)
    self.new.execute(file_path)
  end

  def execute(file_path)
    text = File.read(file_path)
    compound_words, single_words = count_words(text)
    word_count = single_words.map(&:last).inject(:+)
    build_result_text(single_words, compound_words, word_count)
  end

  private

  def count_words(text)
    # 単語の構成文字
    word_char = '[\w’\/-]'
    # Google Play Awards や Clash of Kings のような複合語を検索する
    compound_words = /[A-Z]#{word_char}*(?: of| [A-Z]#{word_char}*)+/
    # 英単語を検索する
    words = /#{word_char}+/
    # 複合語が優先的に検索されるように正規表現を結合する
    regex = Regexp.union(compound_words, words)
    # 検索された複合語や英単語をカウント => ソート => グループ分け
    text.scan(regex)
      .each_with_object(Hash.new(0)) { |word, hash| hash[word] += 1 }
      .sort_by { |word, count| [-count, word.downcase] }
      .partition { |word, _| word.include?(' ') }
  end

  def build_result_text(single_words, compound_words, word_count)
    [
      "単語数(熟語以外):#{word_count}",
      build_words_text('英熟語?', compound_words),
      build_words_text('英単語', single_words)
    ].join("\n")
  end

  def build_words_text(header, count_table)
    [
      "#{header}#{'-' * 66}",
      *count_table.map { |word, count| '%3d %s' % [count, word] }
    ].join("\n")
  end
end

どういう考えでこんなコードにしたのかは、Qiita記事の解説を読めばだいたいわかると思います。

Rubyプログラムを日本語で書いてみる

ところで、風の噂で「プログラミングはやってないけど、伊藤さんのブログはときどき読んでます」という人の話を聞きます。(どうもありがとうございます!)
そんな人は上のようなプログラムを見ても、ちんぷんかんぷんな呪文にしか見えないと思います。

そこでロジックはそのままで、英語になっている部分を全部日本語に変えてみました。
どうでしょう?こんなふうに日本語にするとなんとなくやっていることが見えてこないでしょうか?

クラス 単語抽出
  定義 自身.実行する(ファイルパス)
    自身.新たに作る.実行する(ファイルパス)
  定義おわり

  定義 実行する(ファイルパス)
    テキスト = ファイル.読み込む(ファイルパス)
    複合語, 単語 = 単語を数える(テキスト)
    単語数 = 単語.マップ処理する(&:最後の要素).たたみ込む(:+)
    結果テキストを作成する(単語, 複合語, 単語数)
  定義おわり

  以下プライベート

  定義 単語を数える(テキスト)
    # 単語の構成文字
    単語文字 = '[\w’\/-]'
    # Google Play Awards や Clash of Kings のような複合語を検索する
    複合語 = /[A-Z]#{単語文字}*(?: of| [A-Z]#{単語文字}*)+/
    # 英単語を検索する
    単語 = /#{単語文字}+/
    # 複合語が優先的に検索されるように正規表現を結合する
    正規表現 = 正規表現.結合する(複合語, 単語)
    # 検索された複合語や英単語をカウント => ソート => グループ分け
    テキスト.検索する(正規表現)
      .オブジェクトとともに順に処理する(ハッシュ.新たに作る(0)) { |単語, ハッシュ| ハッシュ[単語] += 1 }
      .ソートする { |単語, 件数| [-件数, 単語.小文字にする] }
      .二つに分ける { |単語, _| 単語.含む?(' ') }
  定義おわり

  定義 結果テキストを作成する(単語, 複合語, 単語数)
    [
      "単語数(熟語以外):#{単語数}",
      単語テキストを作成する('英熟語?', 複合語),
      単語テキストを作成する('英単語', 単語)
    ].連結する("\n")
  定義おわり

  定義 単語テキストを作成する(ヘッダー, 件数テーブル)
    [
      "#{ヘッダー}#{'-' * 66}",
      *件数テーブル.マップ処理する { |単語, 件数| '%3d %s' % [件数, 単語] }
    ].連結する("\n")
  定義おわり
クラスおわり

まあ、英語と日本語を1対1で直訳しただけなので、Ruby(と正規表現)の知識がある程度ないと結局意味不明だとは思います。
ですが、プログラミング未経験の方も、プログラムとはいったい何なのか、上の「日本語プログラム」を読んでなんとなくの雰囲気だけでも感じ取ってもらえると嬉しいです。
僕たちプログラマはこういう文字をパチパチ打ち込んで、コンピューターを動かす仕事をしているわけです。

英語が母国語だとこんな感覚なのかも

僕は10年以上プログラマの仕事をやっていますが、今でもときどき「英語が母国語だとプログラムが全く違って見えるんだろうなあ」と思うことがあります。
僕はまあまあ英語は得意ではあるものの、母国語である日本語の方が書いたり読んだりするのははるかに簡単です。
英語が母国語だと、それこそ上で示した「日本語のRubyプログラム」のような感覚でいろんなソースコードが読み書きできるんでしょうね。
いやあ、うらやましい!

日本語でプログラミングできる言語もある!

さて、こんなことを書いてると、読者のみなさんの中には「日本語でプログラミングできる言語だってあるよ!!」とツッコミたくて、うずうずしている人もたくさんいると思います(苦笑)。
そうなんです。日本語でプログラミングできる言語も世の中にはあるんです。

以下はWikipediaに載っていた「日本語プログラミング言語」の例です。

  • 朱唇(水谷静夫)[6]
  • カナ文字FORTRAN[7]
  • 和漢
  • 日本語AFL(共立出版『アルゴリズム辞典』 pp. 587-589)
  • G-BASIC(ぴゅう太に搭載)
  • Mind
  • 論理的プログラミング言語「論具」(LONG、LOgical Natural lanGuage description)[8]
  • TTSneo、プロデル
  • ドリトル
  • ひまわり、なでしこ
  • 言霊、ことだま on Squeak
  • まほろば[9][10]
  • DNCL(大学入試センター試験「情報関係基礎」)およびTUAT-LE(農工大)
日本語プログラミング言語 - Wikipedia

この中でも「ひまわり」や「なでしこ」は比較的有名な日本語プログラミング言語かもしれません。

日本語でプログラミングするとどうなるんでしょうか?
以下は「なでしこ」の公式ページに載っていた、うるう年を判定するコードです。(出典

年は2000
#(1)4で割れたら閏年の可能性
もし(年%4)=0ならば
  #(2)100で割れたら閏年でない可能性
  もし(年%100)=0ならば
    #(3)400で割れたら閏年
    もし(年%400)=0ならば
      「閏年である」と表示。
    違えば
      「閏年ではない」と表示。
  違えば
    「閏年である」と表示。
違えば
  「閏年ではない」と表示。

これは本当にこのままで動く「日本語のプログラム」です。
日本語プログラミング言語を使うとこんな感じで日本語でプログラムを書いていくことができます。

興味がある方は「なでしこ」やその他の日本語プログラミング言語について調べてみてください。

まとめ

というわけで、今回のエントリではQiitaで公開したプログラムの話から脱線して、人間が読み書きする言語とプログラミング言語の関係性について、ふわっとお話ししてみました。
「プログラミングは難しそう」と思われがちですが、これが自分の母国語だったら、プログラミングに取り組むまでのハードルがちょっと低くなるかもしれませんね。

逆に言うと、一見「謎の呪文だらけ」にしか見えないプログラミングも、実際は英語で「ああしろ、こうしろ」とコンピューターに命令しているだけなんです。
そう考えると、プログラミングに対する心理的ハードルが下がったりしないでしょうか?

まあ、プログラミングには本当に難しい側面もありますが、ぱっと見の印象だけで言えば、母国語かそうでないかの違いは大きいんじゃないかな~と僕は感じています。
こうした情報がプログラミング未経験者の方の参考になれば幸いです。

参考:日本語プログラミング言語や子ども向けプログラミング言語の本

僕はその昔「ひまわり」の本を読んだことがあります。

こちらは読んだことはありませんが、「なでしこ」の本も出ているみたいですね。

日本語プログラム言語なでしこ公式バイブル ver1.5対応版

日本語プログラム言語なでしこ公式バイブル ver1.5対応版

最近では視覚的にプログラミングできる子ども向けプログラミング言語「スクラッチ」も有名です。

小学生からはじめるわくわくプログラミング

小学生からはじめるわくわくプログラミング

小学生からはじめるわくわくプログラミング2

小学生からはじめるわくわくプログラミング2