問題で与えられた式をそのまま使ってメソッドを定義します。
def cels2fahr(cels) return Float(cels) * 9 / 5 + 32 end
(1)の逆の計算を定義します。 「+」と「*」の優先順位の違いから括弧が必要なことに注意してください。
def fahr2cels(fahr) return (Float(fahr) - 32) * 5 / 9 end
単に「rand(6)」とした場合は、0から5の値が返るので結果に1を足します。
def dice return rand(6) + 1 end
まず、2より小さい数は素数でないとします。それ以上の数は、2からその数の平方根まで割り算を行って、すべての剰余が0でないことを確認します。
def prime?(num) return false if num < 2 2.upto(Math.sqrt(num)){|i| if num % i == 0 return false end } return true end
二つの方法を挙げます。もっと小さな配列なら「ary = [1, 2, 3, ...]
」とリテラルで定義してもいいでしょう。
# 空の配列を作って、1から100までの値を格納する ary = [] 100.times{|i| ary[i] = i + 1 } # Rangeオブジェクトのto_aメソッドを使う ary = (1..100).to_a
# 配列を作成する ary = (1..100).to_a # 配列の全ての要素を100倍した値を含む新しい配列を作る ary2 = ary.collect{|i| i * 100 } p ary2 # 配列の全ての要素を100倍する ary.collect!{|i| i * 100 } p ary
# 配列を作成する ary = (1..100).to_a # aryから3の倍数だけを取り出す ary2 = ary.select{|i| i % 3 == 0 } p ary2 # aryから3の倍数以外の数を削除する ary.reject!{|i| i % 3 != 0 } p ary
Array#sort
とArray#sort_by
を使う方法では、ブロックの結果を-1倍することによって、逆にソートされるようにしています。
# 配列を作成する ary = (1..100).to_a # (a) Array#reverseを使う ary2 = ary.reverse p ary2 # (b) Array#sortを使う ary2 = ary.sort{|a, b| -(a <=> b) } p ary2 # (c) Array#sort_byを使う ary2 = ary.sort_by{|i| -i } p ary2
Array#each
を使う場合は値を蓄えておくための変数(例中のresult
)を用意する必要がありますが、Array#inject
を使う場合は必要ありません。
# 配列を作成する ary = (1..100).to_a # (a) Array#eachで和を求める result = 0 ary.each{|i| result += i } p result # (b) Array#injectで和を求める p ary.inject(0){|memo, i| memo += i }
# 配列を作成する ary = (1..100).to_a # randを使って配列をかき混ぜる ary2 = ary.sort_by{|i| rand } p ary2
取り出す要素の先頭のインデックスと必要な要素の数を指定します。
# 配列を作成する ary = (1..100).to_a result = Array.new 10.times{|i| result << ary[i*10, 10] } p result
Array#zip
を使います。
def sum_array(ary1, ary2) result = Array.new ary1.zip(ary2){|a, b| result << a + b } return result end p sum_array([1, 2, 3], [4, 6, 8])
開き括弧に出会ったらをスタックに積んで、閉じ括弧に出会ったときに取り出します。取り出した開き括弧が、閉じ括弧に対応するものかどうかを調べます。
def balanced?(array) stack = Array.new() array.each{|elem| case elem when '(' stack.push(elem) when '{' stack.push(elem) when ')' prev_elem = stack.pop if prev_elem != '(' return false end when '}' prev_elem = stack.pop if prev_elem != '{' return false end else return false end } if stack.empty? return true else return false end end p balanced?([]) #=> true p balanced?(["(",")"]) #=> true p balanced?(["{","(",")","}"]) #=> true p balanced?(["{","(",")"]) #=> false p balanced?(["(",")","}"]) #=> false p balanced?(["(", "{", "{", "}", "(", ")", "}", "(", ")", ")"]) #=> true p balanced?(["(", "{", "{", "}", "(", "}", ")", ")"]) #=> false
str = "Ruby is an object oriented programming language" # (a) 各単語を要素とする配列を作る ary = str.split p ary # (b) 配列をアルファベット順にソートする p ary.sort # (c) 配列を大文字と小文字を区別せずに単語順にソートする p ary.sort_by{|s| s.downcase } # (d) 全ての単語の先頭を大文字にする ary = str.split cap_ary = ary.collect{|word| word.capitalize } p cap_ary.join(" ") # (e) 文字の出現数をカウントする result = Hash.new(0) # 集計用のHashを用意する chars = str.split(//) # 文字列を文字単位に分割する chars.each{|c| result[c] += 1 } # 文字毎に出現回数を数える result.keys.sort.each{|c| puts "'#{c}': #{"*" * result[c]}" }
補足:Array#joinを利用していますが本文中で紹介されていません。 このメソッドは配列に含まれる文字列を連結します。 要素と要素の間に挿入する文字列を引数で指定することもできます。
p ["a", "b", "c"].join #=> "abc" p ["a", "b", "c"].join("-") #=> "a-b-c"
各桁の数字をString#gsub
で置き換えていきます。
def kan2num(string) digit4 = digit3 = digit2 = digit1 = "0" nstring = string.dup nstring.gsub!(/一/, "1") nstring.gsub!(/二/, "2") nstring.gsub!(/三/, "3") nstring.gsub!(/四/, "4") nstring.gsub!(/五/, "5") nstring.gsub!(/六/, "6") nstring.gsub!(/七/, "7") nstring.gsub!(/八/, "8") nstring.gsub!(/九/, "9") if nstring =~ /((\d)?千)?((\d)?百)?((\d)?十)?(\d)?$/ if $1 digit4 = $2 || "1" end if $3 digit3 = $4 || "1" end if $5 digit2 = $6 || "1" end digit1 = $7 || "0" end return (digit4+digit3+digit2+digit1).to_i end p kan2num("七千八百二十三") p kan2num("千八百二十三") p kan2num("八百二十三") p kan2num("百二十三") p kan2num("二十三") p kan2num("十三") p kan2num("三")
この問題はちょっとしたパズルです。各桁の数字を決まった数のアスタリスクに置き換え、前の桁までの部分は10回足し込むことによって10倍します。
def num2astrisk(str) num = "" str.split(//).each{|char| char.sub!("0", "") char.sub!("1", "*") char.sub!("2", "**") char.sub!("3", "***") char.sub!("4", "****") char.sub!("5", "*****") char.sub!("6", "******") char.sub!("7", "*******") char.sub!("8", "********") char.sub!("9", "*********") num = num + num + num + num + num + num + num + num + num + num + char } return num end p num2astrisk("1") p num2astrisk("2") p num2astrisk("3") p num2astrisk("4") p num2astrisk("5") p num2astrisk("10") p num2astrisk("15") p num2astrisk("20") p num2astrisk("25") p num2astrisk("30") p num2astrisk("35") p num2astrisk("40")
# (1) wdayを定義する wday = { "sunday" => "日曜日", "monday" => "月曜日", "tuesday" => "火曜日", "wedensday" => "水曜日", "thursday" => "木曜日", "friday" => "金曜日", "saturday" => "土曜日", } # (2) wdayのサイズを求める p wday.size #=> 7 # (3) wdayのサイズを求める %w(sunday monday tuesday wedensday thursday friday saturday).each{|day| puts "「#{day}」は#{wday[day]}のことです。" }
def str2hash(str) hash = Hash.new() array = str.split(/\s+/) while key = array.shift value = array.shift hash[key] = value end return hash end p str2hash("bule 青 white 白 red 赤");
class OrderedHash def initialize() @keys = Array.new() @content = Hash.new() end def [](key) @content[key] end def []=(key, value) @content[key] = value if !@keys.include?(key) @keys << key end end def delete(key) @keys.delete(key) @content.delete(key) end def keys() @keys end def each() @keys.each{|key| yield(key, @content[key]) } end end oh = OrderedHash.new() oh["one"] = 1 oh["two"] = 2 oh["three"] = 3 oh["two"] = 4 p oh.keys() oh.each{|key,value| p [key, value] }
def get_local_and_domain(str) str =~ /^([^@]+)@(.*)$/ localpart = $1 domain = $2 return [localpart, domain] end p get_local_and_domain("info@example.com")
「難しい」という部分が2回出現しますが、1回の置換で置き換えるのは難しいので2回に分けています。
message = "オブジェクト指向は難しい! なんて難しいんだ!" message.sub!(/難しい/, "簡単だ") message.sub!(/難しいんだ/, "簡単なんだ") puts message
def word_capitalize(str) return str.split(/\-/).collect{|w| w.capitalize}.join('-') end p word_capitalize("in-reply-to") #=> "In-Reply-To" p word_capitalize("X-MAILER") #=> "X-Mailer"
行数、単語数、文字数を出力するwc
メソッドを作成しました。この例ではString#split
を使って行を単語に分割していますが、行頭に空白を含む場合はString#split
の結果に空白の文字列が含まれるため、これを削除していることに注意してください。
def wc(file) nline = nword = nchar = 0 File.open(file){|io| io.each{|line| words = line.split(/\s+/).reject{|w| w.empty? } nline += 1 nword += words.length nchar += line.length } } puts "lines=#{nline} words=#{nword} chars=#{nchar}" end wc(__FILE__)
def tee(input, *outputs) input.each{|line| outputs.each{|io| io.write(line) } } end open(__FILE__){|io| tee(io, $stdout, $stderr) }
def tail(lines, file) queue = Array.new open(file){|io| while line = io.gets queue.push(line) if queue.size > lines queue.shift end end } queue.each{|line| print line } end puts "===" tail(10, __FILE__) puts "===" tail(3, __FILE__)
def print_libraries $:.each{|path| next unless FileTest.directory?(path) Dir.open(path){|dir| dir.each{|name| if name =~ /\.rb$/i puts name end } } } end print_libraries
本文中では詳しく取り上げていませんが、RubyのライブラリにはRubyで記述されたものの他に、C言語などで記述された拡張ライブラリがあります。拡張ライブラリは「.rb
」ではなく、「.dll
」や「.so
」といったプラットフォームによって異なる拡張子を持ったファイル名になっています。
rbconfig
ライブラリから、この拡張子を取得して拡張ライブラリにも対応させたバージョンを以下に示します。
require "rbconfig" def print_libraries $:.each{|path| next unless FileTest.directory?(path) dlext = RbConfig::CONFIG["DLEXT"] Dir.open(path){|dir| dir.each{|name| if name =~ /\.rb$/i || name =~ /\.#{dlext}$/i puts name end } } } end print_libraries
require "find" def du(path) result = 0 Find.find(path){|f| if File.file?(f) result += File.size(f) end } printf("%d %s\n", result, path) return result end du(ARGV[0] || ".")
def jparsedate(str) now = Time.now year = now.year month = now.month day = now.day hour = now.hour min = now.min sec = now.sec str.scan(/(午前|午後)?(\d+)(年|月|日|時|分|秒)/){ case $3 when "年"; year = $2.to_i when "月"; month = $2.to_i when "日"; day = $2.to_i when "時"; hour = $2.to_i hour += 12 if $1 == "午後" when "分"; min = $2.to_i when "秒"; sec = $2.to_i end } return Time.mktime(year, month, day, hour, min, sec) end p jparsedate("2006年12月23日午後8時17分50秒") p jparsedate("12月23日午後8時17分50秒") p jparsedate("午前8時17分50秒") p jparsedate("8時17分50秒")
def ls_t(path) entries = Dir.entries(path) # エントリを取得 entries.reject!{|name| /^\./ =~ name } # "."で始まるファイルを削除 mtimes = Hash.new # mtimeを収集しながらソート entries = entries.sort_by{|name| mtimes[name] = File.mtime(File.join(path, name)) } entries.each{|name| printf("%-40s %s\n", name, mtimes[name]) # ファイル名とmtimeを表示 } rescue => ex puts ex.message end ls_t(ARGV[0] || ".")
文房具の万年カレンダーの要領でカレンダーを整形する例を紹介します。2月30日などの存在しない日付は、月末の日付と比較することで弾いています。また、テーブル中の日付が存在しない部分も同じ条件で弾くために「99」で初期化しています。
require "date" module Calendar WEEK_TABLE = [ [99, 99, 99, 99, 99, 99, 1, 2, 3, 4, 5, 6, 7], [ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], [ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21], [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28], [23, 24, 25, 26, 27, 28, 29, 30, 31, 99, 99, 99, 99], [30, 31, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99], ] module_function def cal(year, month) first = Date.new(year, month, 1) # 指定された月の1日 end_of_month = ((first >> 1) - 1).day # 翌月の1日の前日 start = 6 - first.wday # テーブルの何処から表示するか puts first.strftime("%B %Y").center(21) puts " Su Mo Tu We Th Fr St" WEEK_TABLE.each{|week| buf = "" week[start, 7].each{|day| if day > end_of_month buf << " " else buf << sprintf("%3d", day) end } puts buf } end end t = Date.today Calendar.cal(t.year, t.month)