問題で与えられた式をそのまま使ってメソッドを定義します。
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)