Open43
Paiza勉強中のメモ
標準出力
数値を一行で出力したい場合
x,y =20,30
puts [x,y].join(' ') #=> 20 30
参考資料
puts
メソッドで出力する
配列を- 勝手に改行してくれるので便利
問題
「paiza hello」のように、半角の空白がある文字列を
paiza
hello
のように改行ありで出力する。
解答
- 多重代入を使用
- あらかじめ要素数が決まっている場合はこちらでOK
s, t = gets.split(' ')
puts s #=>paiza
puts t #=>hello
- 配列の出力
s = gets.split(' ')
puts s
#=>paiza
#=>hello
p s =>["paiza", "hello"]
- 配列をputs メソッドで出力しようとすると...
配列や文字列以外のオブジェクトが引数として与えられた場合には、当該オブジェクトを最初に to_ary により配列へ、次に to_s メソッドにより文字列へ変換を試みます。
- Kernel.#puts (Ruby 3.3 リファレンスマニュアル)
- pメソッドだと配列で出力されます。
Integerで配列にしたい場合
#入力値
200 300
#方法
x = gets.split(' ').map(&:to_i)
p x #=>[200,300]
多重代入を使う
#入力
3 5
n,m = gets.split(" ").map(&:to_i)
n#=>3
m#=>5
'one two three four five'.split(' ').each { |val| puts val }
- 配列処理なので単純なものであればそのままブロックに処理を投げることもできる
- Rubyの多重代入あれこれまとめ #Ruby - Qiita
&:
について
- 使用できるケースは基本的に下記3つのケースが揃った場合のみである(チェリー本p114)
- ブロックパラメータが1個だけである
- ブロック内で呼び出すメソッドに引数がない
- ブロック内では、ブロックパラメータに対してメソッドを1回呼び出す以外の処理がない
- すなわち下記のようなケースではこの記法はできない
- 演算子を使用するケース
- ブロック内のメソッドで引数を渡すケース
- ブロック内で複数の文を実行するケース
- 【Ruby】array.map(&:method)を理解する #Ruby - Qiita
&:
を使用せず書く場合
x = gets.split(' ').map{|n| n.to_i}
p x [200,300]
チェリー本の参考になりそうな箇所🍒
繰り返し処理
- pp153-158
N行のデータの入力(N:1行目の数)
#入力
3
hoge
foga
foo
#出力
hoge
foga
foo
n = gets.to_i #=> 最初の要素分繰り返すために数値で代入
n.times do
s = gets.chomp #改行コード除いた状態で代入
puts s #putsメソッドで出力
end
配列の要素同士を足し算する時
- 入力
5 6
結果
11
inject
使ってたたみこみ演算する
ary = gets.split(" ").map(&:to_i)
puts ary.inject {|result, item| result + item }
map
メソッドとsum
メソッドを使う
ary = gets.split(" ")
sum = ary.map(&:to_i).sum
p sum
配列にして各要素に対して足し算する場合
- 入力
5 6
結果
[10,12]
map
メソッド使用
ary = gets.split(" ")
result = ary.map do |n|
n = n.to_i
n += n
end
p result
- ブロック処理の注意点
ary = gets.split(" ")
result = ary.map do |n|
n.to_i
n += n
end
p result
#=>["55", "66"]
-
n.to_i
してもどこかに代入しない限り、n += n
は文字列の連結になってしまうよ。
配列から最小値・最大値を探す
最小値
最大値
宇宙演算子
-
<=>
を使った書き方もできます
問題
#入力(5つの入力限定です)
2
6
8
91
4
#出力
2
- 自分の解答
ary = []
result = 5.times do
ary << gets.to_i
end
p ary.min
- 解答例
n = []
5.times { n.push gets.to_i }
puts n.min
文字列の比較
- 文字列を2つ入力して一致していれば"OK"、異なっていれば"NGを表示
eql?
メソッドを使用
x = gets.chomp
y = gets.chomp
if x.eql?(y)
puts "OK"
else
puts "NG"
end
==
と三項演算子を使う(解答例)
a = gets.chomp
b = gets.chomp
puts a == b ? 'OK' : 'NG'
==
よりeql?
の方が厳密
数値だとputs 1 == 1.0 #=>true
puts 1.eql?(1.0) #=>false
-
eql?
はNumericクラスのインスタンスメソッドを継承しており、クラスが等しいかどうかも含めて判断をする - 一方、
==
は比較演算子で数値として等しいか判定する。
指定値までの繰り返し
改行あり
- 1から100までを改行ありで出力しましょう
times
を使う方法
100.times do |i|
p i + 1
end
upto
使う方法
1.upto(100) { |i|
puts i
}
- 先頭に
puts
つけると最後に1が出力されるよ
puts 1.upto(5) { |i|
puts i
}
#=>1
#=>2
#=>3
#=>4
#=>5
#=>1
puts
で出力する方法
配列作成して
to_a
メソッド(引数は()です。注意。)
n = gets.to_i
puts (1..n).to_a
- Rangeクラスのメソッドになります
- Range#entries (Ruby 3.3 リファレンスマニュアル)
アスタリスク使う方法
n = gets.to_i
puts [*1..n]
- カッコ省略しないと下記の通り
n = gets.to_i
puts [*(1..n)]
-これでも大丈夫
n = gets.to_i
puts *(1..n)
引数の直前に * がついている場合、その引数の値が展開されて渡されます。展開はメソッド to_a を経由して行なわれます。
引用元:メソッド呼び出し(super・ブロック付き・yield) (Ruby 3.3 リファレンスマニュアル)
- 結局
(1..n).to_a
している - 【Ruby】任意の範囲の数値で配列を作る #Ruby - Qiita
横並び
末尾半角スペースあり
1.upto(10){|n| print n," "}
末尾改行スペースなし
9.times do |i|
print i + 1, " "
end
print 10
1.upto(999){
|i| print i," "
}
print 1000
- もっといい書き方ありそうだけど...
複数行の読み込み
IOクラスのメソッドたち
全部読み込む
1行読み込み
-
gets
はEOF(End Of File) に到達した時には nil、readline
はEOFErrorが発生する - IO#gets (Ruby 3.3 リファレンスマニュアル)
- IO#readline (Ruby 3.3 リファレンスマニュアル)
- Ruby 標準入力で複数行を読み込む readlinesメソッド
読み込んで一行改行スペースありで表示する例
#入力
ok
soso
bad
iine
# 出力
ok soso bad iine
n = readlines.map(&:chomp).join(" ")
puts n
繰り返し処理(回数わかっているとき)
#入力
paiza
kirishima
kyoko
#出力
paiza|kirishima|kyoko
arry = []
3.times do |s|
s = gets.chomp
arry << s
end
puts arry.join("|")
繰り返し処理で行う場合
回数がわかっているとき
arry = Array.new(1000)
1000.times{|i|arry[i] = gets.to_i}
arry.each{|x|puts x}
- 配列の要素の番号に代入していくスタイル。他の例でも使えそう。
一列の文字列を区切りありで出力
問題
#入力
123456789
123,456,789
- 問題は必ず3の倍数だった
each_with_index
を使う方法(3の倍数の時のみ)
arry = gets.split(/\s*/).map(&:to_i) #=>\s*は空白文字0文字以上の正規表現。これで一文字ずつの配列を作れる。
arry.each_with_index do |n,idx|
if idx == 0
result = print n.to_s
elsif idx % 3 == 0
result = print "," + n.to_s
else
result = print n.to_s
end
print result
end
- idx % 3 == 0とelseの条件分岐で書いていたのだが、0がtrueになってしまうらしく冒頭にカンマが入ってしまった
- わざわざ数字に変換する必要なかった(文字列に戻しているので)
- Rubyアソシエーション: 正規表現
正規表現使う方法(3の倍数以外の時も可)
n = gets.chomp.reverse.scan(/\d{1,3}/).join(",").reverse
puts n
-
\d{1,3}
にしないと3で割り切れない時に値が消えます
文字数数えるメソッド(String クラス)
length
とsize
は同じ
count
は引数が必要
3*3の出力
問題
#入力
0 1 2 3 4 5 6 7 8
#出力
0 1 2
3 4 5
6 7 8
each_cons
使った解き方
arry = gets.split(" ").map(&:to_i)
arry.each_slice(3){
|num| puts num.join(" ")
}
九九の表作る
書いたコード
(1..9).each do |n|
puts [n*1,n*2,n*3,n*4,n*5,n*6,n*7,n*8,n*9].join(" ")
end
- クソコードだなと思いつつ、ブロック内での処理が書けず断念
GPT先生に聞く
(1..9).each { |n| puts (1..9).map { |i| n * i }.join(" ") }
- 知っているメソッドしかないけど書けない。
九九の表(罫線あり)
column = []
(1..9).each do |n|
row = []
(1..9).each do |m|
row << sprintf("%2d" % (n * m))
end
column << row.join(" | ") + "\n"
end
table = column.join("=" * 42 + "\n")
puts table
-
push
メソッドより<<
の方が個人的によく使うので書き換えてます
すべての行の長さが不定な 2 次元配列の出力
問題
#入力
4
1 3 2 4
#出力
1
1 2 3
1 2
1 2 3 4
自分の回答
n = gets.to_i
arry = gets.split(" ").map(&:to_i)
arry.each do
|i| x = 1.upto(i) #=>ここで出力するとEnumerableになってしまう
puts x.to_a.join(" ")
end
- ちょっとすっきり書く方法
n = gets.to_i
arry = gets.split.map(&:to_i)
arry.each { |i| puts (1..i).to_a.join(" ") }
似たような難しい問題
問題
#入力
10 4 #=>10が要素数、4が分割の個数
1 2 3 4 5 6 7 8 9 10#=>分割する配列
2 6 1 1#=>分ける個数
#出力
1 2
3 4 5 6 7 8
9
10
回答
n,m = gets.split(' ').map(&:to_i) #=>ここの値使ってないなぁ
arry1 = gets.split(' ').map(&:to_i)
arry2 = gets.split(' ').map(&:to_i)
s = 0
arry2.each{|num|
puts arry1[s,num].join(" ")
s += num
}
- ChatGPTにも聞いたけど同じ回答だった
- ブロック処理だとスコープあまり意識しなくて良いので嬉しい
配列内に要素があるかわかるメソッド
三項演算子と組み合わせすっきり書ける
- 食べたいものがmenuの中にあるか
- 最初配列を回して一つずつ確認しようとしてたけどこっちの方が便利
- ただ完全一致しか調べられないので一部一致とかの時は配列回して確認する必要がありそう
menus.include?(want_to_eat)? "Yes" : "No"
include?
使えます
文字列でも
整数か小数かどちらか入力されてそれをIntegerかfloatで出力したい
- 入力値を小数で受け取ってそれを整数値に変換
-
==
で同値であるか確認し、同値であれば整数で、異なれば小数で出力 - ちなみに、
eql?
にしてしまうと1.eql?(1.0)はfalse(クラスも含めて判定するため)になってしまいます
n = gets.to_f
puts n == n.to_i ? n.to_i : n
小数点切り捨て
フォーマット文字列
問題
- 整数の場合は整数、小数の場合は指定された桁数で入力値で桁が足りない場合は0で埋める
#入力①
0.813 2
#出力①
0.81
#入力②
0.813 4
#出力②
0.8130
回答
n, m = gets.split(" ")
num = n.to_f
digits = m.to_i
format_num = sprintf("%.#{digits}f", num)
puts format_num
問題
1行目に要素数、2行目以下は上記と同じような感じ
4
0.813 1
0.813 2
0.813 3
0.813 4
0.8
0.81
0.813
0.8130
回答
x = gets.to_i
x.times do
n,m = gets.split(" ")
num = n.to_f
digits = m.to_i
rounded_num = num.round(digits) #この行ないと1ケース失敗した
format_num = sprintf("%.#{digits}f", rounded_num)
puts format_num
end
- 先に丸め処理をしてからフォーマット文字列にした方が良い?
- Float#round (Ruby 3.3 リファレンスマニュアル)
数字3桁分で出力
n = gets.to_i
puts sprintf("%3d" % n)
n = gets.to_i
# 出力
puts("%3d" % n)
式展開が必要な例
#入力
813 3
813 4
#出力
813
813 #4桁分で出力
n,m = gets.split(" ").map(&:to_i)
puts sprintf("%#{m}d", n)
数字3桁分で前の箇所を0で埋める
n = gets.to_i
puts sprintf("%03d", n)
ペアの数値の入った表を罫線入りで出力
問題
#入力
2 3 7 8
#出力
(7, 8) | (7, 8) | (7, 8)
========================
(7, 8) | (7, 8) | (7, 8)
回答
n,m,x,y = gets.chomp.split(" ").map(&:to_i)
row = Array.new(m, "(#{x}, #{y})")
column = []
n.times do
column << row.join(" | ") + "\n"
end
l = row.join(" | ") .length
table = column.join("=" * l + "\n")
puts table
各9桁のフォーマット文字列のバージョン
n,m,x,y = gets.chomp.split(" ").map(&:to_i)
a = sprintf("%9d",x)
b = sprintf("%9d",y)
row = Array.new(m, "(#{a}, #{b})")
column = []
n.times do
column << row.join(" | ") + "\n"
end
l = row.join(" | ") .length
table = column.join("=" * l + "\n")
puts table
配列の最大値
- maxメソッド
- Array#max (Ruby 3.3 リファレンスマニュアル)
デバックに使える
入力値で不要な要素がある場合
#入力
5 1 2 3 4 5
#出力
1
2
3
4
5
- 頭の5は要素数で、出力する際使用しない値
可変長引数使う
n, *m = gets.split(" ").map(&:to_i)
puts m #m.each{|val| puts val}
shift
を使う
a = gets.split(" ").map(&:to_i)
a.shift
puts a #a.each{|val| puts val}
Array.newでインデックスを使って
n = gets.to_i
a = Array.new(n)
b = Array.new(n)
n.times { |i| a[i], b[i] = gets.split(' ').map(&:to_i) }
- 多重代入を使うとスッキリ書けるなぁ
二次元配列を使った回答
問題
#入力
2
1 2 3
8 1 3
#出力
1 2 3
8 1 3
- 各段の数字の個数は3で固定
回答
n = gets.to_i
arry = Array.new(n).map { Array.new(3) }
p arry #=>[[nil,nil,nil],[nil,nil,nil]]
n.times do |i|
row = gets.split(" ").map(&:to_i)
3.times{|j| arry[i][j] = row[j]}
end
arry.each { |row| puts row.join(' ') }
-
arry[0][0]
なら[[★,nil,nil],[nil,nil,nil]]
で★の箇所に代入される
3*3で配列から要素を取り出す
# すべての3x3ブロックを取り出す
arr = [[1,2,3,4,5],[1,2,3,4,5],[1,2,3,4,5],[1,2,3,4,5],[1,2,3,4,5],]
(0..2).each do |i| # 行の開始位置(0から2まで)
(0..2).each do |j| # 列の開始位置(0から2まで)
# 3x3のブロックを取得
sub_array = arr[i, 3].map { |row| row[j, 3] }
p sub_array
end
end
#=>[[1, 2, 3], [1, 2, 3], [1, 2, 3]]
#=>[[2, 3, 4], [2, 3, 4], [2, 3, 4]]
#=>[[3, 4, 5], [3, 4, 5], [3, 4, 5]]
#=>[[1, 2, 3], [1, 2, 3], [1, 2, 3]]
#...
クラス・構造体メニュー①(表示)
問題
#入力例
1
koko 23 04/10 tokyo
#出力例
User{
nickname : koko
old : 23
birth : 04/10
state : tokyo
}
回答
n = gets.to_i
class User
attr_accessor :nickname, :old, :birth, :state
def initialize(nickname, old, birth, state)
@nickname = nickname
@old = old
@birth = birth
@state = state
end
def to_s
"User{\nnickname : #{@nickname}\nold : #{@old}\nbirth : #{@birth}\nstate : #{@state}\n}"
end
end
def parse_input(input)
# 入力を分割して各部分を取得
parts = input.split(" ")
nickname = parts[0]
old = parts[1].to_i
birth = parts[2]
state = parts[3]
# Userオブジェクトを作成して返す
User.new(nickname, old, birth, state)
end
n.times do
input = gets
user = parse_input(input)
puts user
end
- 無理やり配列でなんでも解かずにこうやってオブジェクト指向言語の良さを活かして問題解けるようになりたいぜ
クラス・構造体メニュー①(検索)
問題
#入力
3 #人数
mako 13 08/08 nara #名前・年齢・生年月日・出身地
megumi 14 11/02 saitama
taisei 16 12/04 nagano
14 #=>この年齢の人を出力
#出力
megumi
- クラス使って解いてみる
回答
n = gets.to_i
class User
attr_accessor :name, :old, :birth, :state
def initialize(name, old, birth, state)
@name = name
@old = old
@birth = birth
@state = state
end
end
def parse_input(input)
parts = input.split(" ")
name = parts[0]
old = parts[1].to_i
birth = parts[2]
state = parts[3]
user = User.new(name, old, birth, state)
end
users = []
n.times do |i|
input = gets.chomp
users << parse_input(input)
end
m = gets.to_i
#パターン1
menbers = []
n.times{|i|users[i].old == m ? menbers << users[i].name : menbers }
menbers.map{|val| puts val}
#パターン2
menbers = users.select { |user| user.old == m }.map(&:name)
menbers.each { |name| puts name }
- 年齢の取得のタイミングが後になってしまうので配列2回回してしまう
Ruby関連(Typing Plusより)
条件分岐を一行で書きたい
do_something if condition
- これあんま使ってなかったけどスッキリかけるね
whenでの範囲オブジェクト
when (1..5)
年齢で比較
問題
#入力例
3
mako 13 08/08 nara
taisei 16 12/04 nagano
megumi 14 11/02 saitama
#出力例
mako 13 08/08 nara
megumi 14 11/02 saitama
taisei 16 12/04 nagano
回答
n = gets.to_i
class User
attr_accessor :name, :age, :birth, :state
def initialize(name, age, birth, state)
@name = name
@age = age
@birth = birth
@state = state
end
def to_s
"#{@name} #{@age} #{@birth} #{@state}"
end
end
def parse_input(input)
parts = input.split(" ")
name = parts[0]
age = parts[1].to_i
birth = parts[2]
state = parts[3]
user = User.new(name, age, birth, state)
end
users = []
n.times do
input = gets
user = parse_input(input)
users << user
end
sorted_users = users.sort_by { |user| user.age }
sorted_users.each do |user|
puts user #=>勝手にto_sしてくれる
#=>mako 13 08/08 nara
#=>megumi 14 11/02 saitama
#=>taisei 16 12/04 nagano
end
- Object#to_s (Ruby 3.3 リファレンスマニュアル)
- そもそも
puts
メソッドが「配列や文字列以外のオブジェクトが引数として与えられた場合には、当該オブジェクトを最初に to_ary により配列へ、次に to_s メソッドにより文字列へ変換を試みます。」だからか。 - Kernel.#puts (Ruby 3.3 リファレンスマニュアル)
正規表現で便利グッズ
構造体の書き換え
問題
#入力例
3 2
mako 13 08/08 nara
taisei 16 12/04 nagano
megumi 14 11/02 saitama
2 taihei
3 megu
#出力例
mako 13 08/08 nara
taihei 16 12/04 nagano
megu 14 11/02 saitama
- 指定した要素を書き換えるよ〜
回答
n, m = gets.split(" ").map(&:to_i)
class User
attr_accessor :name, :old, :birth, :state
def initialize(name, old, birth, state)
@name = name
@old = old
@birth = birth
@state = state
end
def to_s
"#{@name} #{@old} #{@birth} #{@state}"
end
end
def parse_input(input)
parts = input.split(" ")
name = parts[0]
old = parts[1].to_s
birth = parts[2]
state = parts[3]
user = User.new(name, old, birth, state)
end
users = []
n.times do
input = gets
user = parse_input(input)
users << user
end
m.times do
parts = gets.split(" ")
num = parts[0].to_i
new_name = parts[1]
users[num - 1].name = new_name
end
puts users
名前変更の箇所のメソッドを切り分ける(ChatGPT)
n, m = gets.split(" ").map(&:to_i)
class User
attr_accessor :name, :old, :birth, :state
def initialize(name, old, birth, state)
@name = name
@old = old
@birth = birth
@state = state
end
def change_name(new_name)
@name = new_name
end
def to_s
"#{@name} #{@old} #{@birth} #{@state}"
end
end
def parse_input(input)
parts = input.split(" ")
name = parts[0]
old = parts[1].to_s
birth = parts[2]
state = parts[3]
User.new(name, old, birth, state)
end
users = []
n.times do
input = gets
user = parse_input(input)
users << user
end
m.times do
parts = gets.split(" ")
num = parts[0].to_i
new_name = parts[1]
users[num - 1].change_name(new_name) # 名前の変更をメソッドに委譲
end
puts users
条件に応じて出力変える
問題
#入力
7
make 2742 mako
getnum 1
make 2782 taisei
getname 2
make 31 megumi
getname 1
getname 3
#出力
2742
taisei
mako
megumi
- makeならデータ作成
- getnameなら名前取得
- getnumなら番号取得
回答
n = gets.to_i
class Employee
attr_accessor :number, :name
def initialize(number, name)
@number = number
@name = name
end
end
def data_input(input)
number = input[1]
name = input[2]
Employee.new(number,name)
end
employees = []
n.times do
input = gets.split(" ")
if input[0] == "make"
employee = data_input(input)
employees << employee
elsif input[0] == "getname"
num = input[1].to_i - 1
puts employees[num].name
else
num = input[1].to_i - 1
puts employees[num].number
end
end
- 配列代入だから
def data_input(*input)
にしたら、メソッドの中で二次元配列になっていた
改善版(ChatGPTにもらったの少し直した)
n = gets.to_i
class Employee
attr_accessor :number, :name
def initialize(number, name)
@number = number
@name = name
end
end
employees = []
n.times do
input = gets.split(" ")
command = input[0]
case command
when "make"
number = input[1]
name = input[2]
employees << Employee.new(number, name) #インスタンスの作成これでいいな
when "getname", "getnum"
index = input[1].to_i - 1
if command == "getname"
puts employees[index].name
else
puts employees[index].number
end
end
end
動的計画法超入門(DP)
値の変更も行う
n = gets.to_i
class Employee
attr_accessor :name, :number
def initialize(name, number)
@name = name
@number = number
end
end
employees = []
n.times do
input = gets.split(" ")
command = input[0]
case command
when "make"
number = input[1].to_i
name = input[2]
employees << Employee.new(name, number)
when "getname", "getnum"
output = input[1].to_i - 1
if command == "getname"
puts employees[output].name
else
puts employees[output].number
end
when "change_num", "change_name"
num = input[1].to_i - 1
change = input[2]
if command == "change_name"
employees[num].name = change
else
employees[num].number = change.to_i
end
end
end
- 書いててわかるごちゃごちゃや
リファクタリング(ChatGPT)
class Employee
attr_accessor :name, :number
def initialize(name, number)
@name = name
@number = number
end
end
# 社員を作成
def make_employee(employees, number, name)
employees << Employee.new(name, number)
end
# 社員情報を取得
def get_employee_info(employees, command, index)
employee = employees[index]
case command
when "getname"
puts employee.name
when "getnum"
puts employee.number
end
end
# 社員情報を変更
def change_employee_info(employees, command, index, change)
employee = employees[index]
case command
when "change_name"
employee.name = change
when "change_num"
employee.number = change.to_i
end
end
n = gets.to_i
employees = []
n.times do
input = gets.split(" ")
command, index = input[0], input[1].to_i - 1
case command
when "make"
make_employee(employees, input[1].to_i, input[2])
when "getname", "getnum"
get_employee_info(employees, command, index)
when "change_name", "change_num"
change_employee_info(employees, command, index, input[2])
end
end
- メソッドで書き分けるの優秀すぎる〜〜
継承
- ChatGPTのコードを元に変更をして学習(元のコードはCustomerクラスが別であってそれを成人と未成年で継承する感じだった)
-
super
って便利だなぁ
class MinorCustomer
attr_reader :total_amount
def initialize(age)
@age = age
@total_amount = 0
end
def order_food(amount)
@total_amount += amount
end
def order_softdrink(amount)
@total_amount += amount
end
def order_alcohol(_amount)
# 未成年なのでお酒の注文はキャンセルされる
# こちらの引数はキャンセルになるので_がついている
end
end
class AdultCustomer < MinorCustomer
def initialize(age)
super(age)
# 成人はお酒割引があるので追加でインスタンス変数の設定が必要
@alcohol_discount = false
end
def order_alcohol(amount)
# 成人はお酒を注文できる
@total_amount += amount
@alcohol_discount = true
end
def order_food(amount)
# お酒を頼んだ場合は200円引き
if @alcohol_discount
amount -= 200
end
super(amount)
end
end
# 計算のためのメソッド
def calculate_total(customers, orders)
orders.each do |order|
# 分割代入
customer_id, order_type, amount = order
customer = customers[customer_id - 1] # インデックスは0始まり
case order_type
when 'food'
customer.order_food(amount)
when 'softdrink'
customer.order_softdrink(amount)
when 'alcohol'
customer.order_alcohol(amount)
end
end
customers.map(&:total_amount)
end
# 標準入力から値を取得
n_customers, n_orders = gets.split.map(&:to_i)
# 年齢の一覧を取得
ages = n_customers.times.map { gets.to_i }
# thenはsplit の結果として得られた配列を次のブロックに渡す。下記と同じ。
# orders = n_orders.times.map do
# o = gets.split
# [o[0].to_i, o[1], o[2].to_i]
# end
orders = n_orders.times.map { gets.split.then { |o| [o[0].to_i, o[1], o[2].to_i] } }
# お客さんを初期化
customers = ages.map do |age|
if age >= 20
AdultCustomer.new(age)
else
MinorCustomer.new(age)
end
end
# 会計を計算
result = calculate_total(customers, orders)
# 出力
result.each { |total| puts total }
- thenメソッド(【Ruby】便利な then(yield_self)メソッドについて)
デフォルト値あり
class MinorCustomer
attr_reader :total_amount
def initialize(age)
@age = age
@total_amount = 0
end
def order_food(amount)
@total_amount += amount
end
def order_softdrink(amount)
@total_amount += amount
end
def order_alcohol(_amount)
end
end
class AdultCustomer < MinorCustomer
def initialize(age)
super(age) #ここのsuperがageのみで良いのはなぜだ(引数があるやつだけでいいのか)
@alcohol_discount = false
end
def order_food(amount)
if @alcohol_discount
amount -= 200
end
super(amount)
end
def order_alcohol(amount: 500)
@total_amount += amount
@alcohol_discount = true
end
end
def calculate_total(customers, orders)
orders.each do |order|
customer_id, order_type, amount = order
customer = customers[customer_id - 1]
case order_type
when 'alcohol', "0"
customer.order_alcohol(amount: amount || 500)
when 'food'
customer.order_food(amount)
when 'softdrink'
customer.order_softdrink(amount)
end
end
customers.map(&:total_amount)
end
n_customers, n_orders = gets.split(" ").map(&:to_i)
ages = n_customers.times.map { gets.to_i }
orders = n_orders.times.map do
o = gets.split
[o[0].to_i, o[1], o[2] == nil ? nil : o[2].to_i]
end
customers = ages.map do |age|
if age >= 20
AdultCustomer.new(age)
else
MinorCustomer.new(age)
end
end
result = calculate_total(customers, orders)
result.map{|total| puts total}
ordersのamoutの変換について
[[2, "0", nil], [2, "food", "4333"], [1, "0", nil], [2, "0", nil], [1, "food", "4606"]]
- 上記の配列の3つ目の要素を、
数値の場合はInteger
でnilの場合はnilのまま
にしてアルコール購入時のデフォルト値を渡せるようにしたかった - 便利な三項演算子
[o[0].to_i, o[1], o[2] == nil ? nil : o[2].to_i]
デフォルト値の渡し方
def order_alcohol(amount: 500)
@total_amount += amount
@alcohol_discount = true
end
#...
customer.order_alcohol(amount)
#...
- 上記だと
Main.rb:33:in
order_alcohol': wrong number of arguments (given 1, expected 0) (ArgumentError)`のエラーが出る
def order_alcohol(amount = 500)
@total_amount += amount
@alcohol_discount = true
end
#...
customer.order_alcohol(amount)
#...
- 上記だと
Main.rb:34:in
+': nil can't be coerced into Integer (TypeError)`のエラーが出る
解決策1
- そもそもメソッドに
nil
が渡らないようにする - これなら別にデフォルト値の設定不要(
nil
なら500が引数になる)
#...
def order_alcohol(amount)
@total_amount += amount
@alcohol_discount = true
end
#...
when 'alcohol', "0"
customer.order_alcohol(amount || 500)
#...
- キーワード引数バージョンもあり(1つなので微妙ですが)
#...
def order_alcohol(amount:)
@total_amount += amount
@alcohol_discount = true
end
#...
when 'alcohol', "0"
customer.order_alcohol(amount: amount || 500)
#...
解決策2
- そもそも条件分岐を分ける
#...
def order_alcohol(_amount = 500) #ここにもデフォルト値置いとかないとエラーになる
end
end
#...
def order_alcohol(amount = 500)
@total_amount += amount
@alcohol_discount = true
end
#...
case order_type
when 'alcohol'
customer.order_alcohol(amount)
when "0"
customer.order_alcohol
when 'food'
customer.order_food(amount)
when 'softdrink'
customer.order_softdrink(amount)
end
when節で複数の条件書くとき
-
when 'alcohol', "0"
といったように並列で書く
辞書
クラスでやる
#入力値
2
Kirishima 1
Kyoko 2
Kirishima
#出力値
1
class Person
attr_accessor :person_id, :property
def initialize(person_id, property)
@person_id = person_id
@property = property
end
end
n = gets.to_i
member = []
n.times do
o = gets.split
property, person_id = o[1], o[0]
member << Person.new(property, person_id)
end
m = gets.chomp
puts member.select { |s| s.property == m }.map(&:person_id)
- 最後の行を
puts menber.map{|s| s.person_id if s.property == m }
って書いたら違うと言われた - ひとまず条件で
select
してオブジェクトを確定してから、該当するもののperson_id
を呼び出す流れ
ハッシュで書く
n = gets.to_i
properties = {}
n.times do
s, a = gets.split(' ')
properties[s] = a.to_i
end
s = gets.chomp #chompしないと改行コード含まれる
puts properties[s]
別バージョン
n = gets.to_i
properties = {}
n.times do
s = gets.chomp
properties[s] = 0
end
m = gets.to_i
m.times do
s, a = gets.split(" ")
properties[s] += a.to_i
end
l = gets.chomp
puts properties[l]
- キーをシンボルにすべきでしょうか...
アルファベット降順
N = gets.to_i
members = {}
N.times { members[gets.chomp] = 0 }
M = gets.to_i
M.times do
s, a = gets.split
members[s] += a.to_i
end
puts members.sort.to_h.values
ハッシュの配列
- 要素が二つある場合の表作るのに便利
- 結構ごちゃごちゃになりますが...
n.times do |i|
lines_num = i + 1
arrays["arry#{lines_num}"] = []
lines = gets.split(" ").map(&:to_i)
arrays["arry#{lines_num}"] << lines
end
LEET問題解法参考🌿
- 特定の文字を置き換える
10000以上で13で割れるものを探す
- あんまりrubyで配列以外の繰り返し処理書いてこなかったので新鮮だ
whileバージョン
n = 10_000
while true
break if n % 13 == 0
n += 1
end
puts n
untilバージョン
number = 10000
until number % 13 == 0 do
number += 1
end
p number
辞書ファイナル
失敗
A, B, C = gets.split(" ").map(&:to_i)
job_members = [] # 配列に変更
A.times do
a, b = gets.split(" ").map(&:to_i)
job_member = {}
job_member["A"] = a
job_member["B"] = b
job_members << job_member
end
job_members.each do |hash|
# 1つ目と2つ目の値を入力
b_value, c_value = gets.split.map(&:to_i)
# "B"の値と入力された1つ目の値が一致する場合に"C"を追加
if hash["B"] == b_value
hash["C"] = c_value
end
end
sorted_job_members = job_members.sort_by { |hash| hash["A"] }
sorted_job_members .each do |hash|
puts "#{hash['A']} #{hash['C']}"
end
正解
- 最後の行が重要
-
group_b[group_a[i + 1]]
でgroup_aのバリューをキーを0から順番に呼び出し、呼び出したgroup_aのバリューがキーになるgroup_bの値を呼び出すことで紐付ける - 頭こんがらがる
n, m, l = gets.split(' ').map(&:to_i)
group_a = {} #AからB
n.times do
num, req = gets.split(' ').map(&:to_i)
group_a[num] = req
end
group_b = {} #BからC
m.times do
num, req = gets.split(' ').map(&:to_i)
group_b[num] = req
end
group_c = {} # 0回目:group_c[1]=>1 = group_b[group_a[1]] =>group_a[1]=2(キーが1のバリュー)=>group_b[2]=2!!!(キーが2のバリュー)
n.times { |i| group_c[i + 1] = group_b[group_a[i + 1]] }
group_c.each do |val|
puts val.join(' ')
end
シミュレーション
- 両方必要だった
h = gets.to_i
paiza = [0, 1, 1]
monster = [0, 1, 1]
h -= 2
turn = 2
while true
# 代入
monster[0] = monster[1]
monster[1] = monster[2]
paiza[0] = paiza[1]
paiza[1] = paiza[2]
monster[2] = paiza[1] + paiza[0]
paiza[2] = monster[1] * 2 + monster[0]
h -= paiza[2]
turn += 1
break if h <= 0
end
puts turn