ツナワタリマイライフ

日常ネタから技術ネタ、音楽ネタまで何でも書きます。

言語処理100本ノックの最初5問をRubyで解く

はじめに

言語処理100本ノックというものがあります。

www.cl.ecei.tohoku.ac.jp

言語処理100本ノックは,実践的な課題に取り組みながら,プログラミング,データ分析,研究のスキルを楽しく習得することを目指した問題集です

言語はPythonを想定しているとのことですが、自分が使うRubyで。言語処理100本ノック自体は以前会社の勉強会で同期が紹介してくれていたので知っていました。

00. 文字列の逆順

文字列"stressed"の文字を逆に(末尾から先頭に向かって)並べた文字列を得よ.

reverse一発。

puts "stressed".reverse
take@MacBook-Air ~/ruby> ruby 00.rb 
desserts

01. 「パタトクカシーー」

「パタトクカシーー」という文字列の1,3,5,7文字目を取り出して連結した文字列を得よ.

str = "パタトクカシーー"
str1 = ""
str2 = ""

0.step(str.length-1,2){|n|
str1 << str[n]
str2 << str[n+1]
}

puts str1
puts str2
take@MacBook-Air ~/ruby> ruby 01.rb 
パトカー
タクシー

loopを使いましたがイケてない感。。。

02. 「パトカー」+「タクシー」=「パタトクカシーー」

「パトカー」+「タクシー」の文字を先頭から交互に連結して文字列「パタトクカシーー」を得よ.

str1 = "パトカー"
str2 = "タクシー"
str = ""
for i in 0..(str1.length - 1) do
  str << str1[i]
  str << str2[i]
end

puts str
take@MacBook-Air ~/ruby> ruby 02.rb 
パタトクカシーー

これも前の問題と同じでloopを使いました。

03. 円周率

"Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."という文を単語に分解し,各単語の(アルファベットの)文字数を先頭から出現順に並べたリストを作成せよ.

str = "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."
word_count = []
word_count = str.split(/[^\w]/).map do|word|
  word.length if word.length > 0
end

p word_count.compact
take@MacBook-Air ~/ruby> ruby 03.rb 
[3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9]

これはかなりイケてない。splitで非言語文字以外で区切ったところ、カンマとスペースが続いてる部分で空文字列が配列要素に生まれてしまった。mapを使って、空文字列がnilとして保存されたのでcompactで出力できたけど、これはイケてないね。。。

04. 元素記号

"Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."という文を単語に分解し,1, 5, 6, 7, 8, 9, 15, 16, 19番目の単語は先頭の1文字,それ以外の単語は先頭に2文字を取り出し,取り出した文字列から単語の位置(先頭から何番目の単語か)への連想配列(辞書型もしくはマップ型)を作成せよ.

str = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."

words = str.split(/[\W]/).reject{|word|
  word.empty?
}

hash = {}
num = [1,5,6,7,8,9,15,16,19,19]
words.each_with_index{|word,i|
  if num.include?(i+1)
    hash[word[0]] = i+1
  else
    hash[word[0,2]] = i+1
  end
}

puts hash
take@MacBook-Air ~/ruby> ruby 04.rb 
{"H"=>1, "He"=>2, "Li"=>3, "Be"=>4, "B"=>5, "C"=>6, "N"=>7, "O"=>8, "F"=>9, "Ne"=>10, "Na"=>11, "Mi"=>12, "Al"=>13, "Si"=>14, "P"=>15, "S"=>16, "Cl"=>17, "Ar"=>18, "K"=>19, "Ca"=>20}

03問目でもあったsplitしたあとの空文字列の除去はrejectを使いました。そもそもsplitのやりかたがイケてないのかもしれない。

あとは単にeach_with_indexでhashに格納していきました。

他のひとの答えを見てみる

言語処理100本ノックを敢えてRubyで (1) - 世界線航跡蔵

01問

puts 1.step(7, 2).map{|i| str[i]}.join  

stepに対してmapをしてるのが何をやってるのかわからない。。。stepはEnumeratorを返してる。これは1,3,5,7の数字の集合なのかな。mapしたあと文字列から該当位置の文字を取り出してjoinで配列から文字列へ。うーん自分では思いつかないな。

02問

strs = %w[パトカー タクシー]  
puts strs.map(&:chars).inject(&:zip).flatten.join  

この書き方はじめて見たんですけど、to_proc (Symbol)って言うんですね。

ref.xaio.jp

確かに、便利だ。。。

03問

sentence = "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."  
p sentence.split(/\W+/).map(&:size)  

1行。。。そうか、splitするとき、¥Wの非言語文字を1文字以上にすればいいのか。。。納得。

おわりに

これ、いろんな言語で解く&みんなで持ち寄ってやいやいレビューするってのをやりたいです。絶対楽しそうだし、いろんな発見がありそう。自分で解いたあとにひとのコードを見るってのがいいですね。

あとPythonこれから触っていきたいのでPythonでも挑戦したいな。