Ruby
本書は、Rubyのチュートリアルです。 Rubyは、比較的習得が容易なオブジェクト指向スクリプティング言語です。 まつもとゆきひろによって開発されました。 Ruby on Railsの記述言語として選ばれたことで有名になりましたが、ウェブアプリケーション以外にもシステム管理やネットワークアプリケーションなどさまざまな用途に用いられます。
準備編集
rubyインタープリターの準備編集
まずはrubyインタープリターを用意しましょう[1]。 インタープリターとは、あなたが書いたスクリプトを読んでそれを実行してくれるソフトウェアです。 Ruby公式サイトの「ダウンロード」ページから、 それぞれの環境に合ったインタープリターをダウンロードして下さい。 具体的なインストール手順は環境によって違うので、「Rubyのインストール」を参照して下さい。
準備ができたら、試しにコマンドラインからインタープリターを呼び出してみましょう。
- バージョンを確かめる
% ruby --version ruby 3.2.0 (2022-12-25 revision a528908271) [amd64-freebsd13]
- バージョンは、環境によって異なります。
2022年12月現在、ruby-1.x は全てEoL(End-of-Life;サポート終了)、 ruby-2.x は 2.7 がセキュリティメンテナンス、それ以外はEoL、 ruby-3.x は 3.0 - 3.2 がノーマルメンテナンス、3.2 がプレビューです[2]。
もしご自身の環境の ruby のバージョンが既にサポート終了しているのであれば、サポートされているバージョンへのアップデートを検討してください。 本書では ruby-2.7 以降を想定しています。
- ミススペルすると
% ruby --vertion ruby: invalid option --vertion (-h will show valid options) (RuntimeError)
- versionの綴りが違うとこの様なメッセーが出ます。案内に従って
ruby -h
を実行してみましょう。 - コマンドラインオプションの説明
% ruby -h Usage: ruby [switches] [--] [programfile] [arguments] -0[octal] specify record separator (\0, if no argument) -a autosplit mode with -n or -p (splits $_ into $F) -c check syntax only -Cdirectory cd to directory before executing your script -d set debugging flags (set $DEBUG to true) -e 'command' one line of script. Several -e's allowed. Omit [programfile] -Eex[:in] specify the default external and internal character encodings -Fpattern split() pattern for autosplit (-a) -i[extension] edit ARGV files in place (make backup if extension supplied) -Idirectory specify $LOAD_PATH directory (may be used more than once) -l enable line ending processing -n assume 'while gets(); ... end' loop around your script -p assume loop like -n but print line also like sed -rlibrary require the library before executing your script -s enable some switch parsing for switches after script name -S look for the script using PATH environment variable -v print the version number, then turn on verbose mode -w turn warnings on for your script -W[level=2|:category] set warning level; 0=silence, 1=medium, 2=verbose -x[directory] strip off text before #!ruby line and perhaps cd to directory --jit enable JIT for the platform, same as --mjit (experimental) --mjit enable C compiler-based JIT compiler (experimental) -h show this message, --help for more info
- これもバージョンによって表示が異なります。
[TODO:其々のコマンドラインオプションの説明は割愛しましたが、ワンライナーを書くときに役に立つ -nle など代表的な組合わせをコラム(?)で紹介すべきでしょう]
Hello, World!編集
他の多くのチュートリアルがそうであるように、 私たちもまずはRubyの世界にあいさつすることから始めましょう。 hello.rbというファイルを作り(Rubyではスクリプトファイルに.rbという拡張子を付けることが通例となっています)、次のように書いて保存して下さい。
- hello.rb
puts 'Hello, World!'
それではこのスクリプトを実行してみましょう。コマンドラインから次のようにタイプして下さい。
% ruby hello.rb Hello, World! %
この1行のスクリプトは、メソッドputs
に文字リテラル'Hello, World!'
を渡し呼出しています。
irb編集
irb(Interactive Ruby) は、Rubyを対話的に実行するためのコマンドシェル[3]です[4]。
- irbで 'Hello, World!'
% irb irb(main):001:0> puts "Hello, world!" Hello, world! => nil irb(main):002:0> exit %
- シェルから irb を起動
puts "Hello, world!"
と入力し、エンターを押して評価しています。Hello, world!
はputs
メソッドの出力です。=> nil
は、puts
メソッドの戻値です。exit
で irb を終了しています。- シェルのプロンプトに復帰
irbのインストール編集
環境によっては、rubyインタプリターとは別にirbを追加でインストールしないと、irbは使えない場合もあります。
もし、irb
がインストールされていないのであれば、実行環境のパッケージマネージャーで irb を検索しインストールしてください。
また、rubygems にも irb のパッケージがあるので
% sudo gem install irb
でインストールできます。
変数編集
オブジェクトを変数に代入するには
変数名 = オブジェクト
の様に代入演算子 =
を使います。
- 変数へのオブジェクトの代入と参照
a = 1 p a a = "abc" p a a = [1, 2, 3] p a b = a p b b[1] = 999 p a
- 表示結果
1 "abc" [1, 2, 3] [1, 2, 3] [1, 999, 3]
- 変数 a に整数 1 を代入
- メソッド p に a を引数とした呼出し
- 変数 a を使った参照
1
が表示される
- 変数 a に文字列 "abc" を代入
"abc"
が表示される- 変数 a に配列 [1, 2, 3] を代入
[1, 2, 3]
が表示される- 変数 b に変数 a が参照しているオブジェクトを代入
- ここでは、配列 [1, 2, 3]
- やはり、
[1, 2, 3]
が表示される - 変数 b の参照している配列の1番目の要素に 999 を代入
- これは変数ではなく配列の要素への代入
- 配列の先頭は0番目
- 変数 a を使って参照すると
- 1番目の要素が 999 に書換わって
[1, 999, 3]
となっている
- 1番目の要素が 999 に書換わって
代入は、オブジェクトに後から参照するための名前(変数名)をつけることに他なりません。 このため、変数の参照するオブジェクトを別の変数に代入すると「別名」を作ることになります。
puts メソッドと p メソッド |
putsメソッドはオブジェクトにto_sメソッドで適用した結果を表示します。
これに対し、pメソッドはオブジェクトにinspectメソッドで適用した結果を表示します。
|
オブジェクト編集
Ruby では、多くのものがオブジェクトで、オブジェクトは1つのクラスに属しています。 他のオブジェクト指向プログラミング言語では、整数や文字列などはメソッドをプリミティブとして別扱いし、 ラッパーをオブジェクトを用意することでメソッドがないことを補っていますが、 Ruby では整数や文字列など基本的なデータ型もオブジェクトでメソッドを持ちます。
オブジェクトのクラスを調べる編集
オブジェクトのクラスを調べるには、 .class メソッドを使います。
$ irb
irb(main):001:0> 0.class
=> Integer
irb(main):002:0> "abc".class
=> String
irb(main):003:0> [1,2,3].class
=> Array
irb(main):004:0> ({a:2, b:3, c:5, d:7}).class
=> Hash
irb(main):005:0> (1...99).class
irb(main):005:0> (1...99).class
=> Range
全てのクラスのスーパークラス Object編集
Object は全てのクラスのスーパークラスです。
式と演算子編集
式 (expression) や演算子 (operator) について説明します。
Rubyにはい可能な演算子があります。
- 代入演算子
- 比較演算子
- 算術演算子
- ビット演算子
- 論理演算子
- 文字列演算子
- 条件(三項)演算子
- カンマ演算子
オブジェクトとメソッド演算子編集
演算子はオブジェクトのメソッドの特殊な形で、二項演算子の場合は左辺のオブジェクトがレシーバーになります。
Object(==, ===, =~) Array(+, -, *, &, <<, <=>, ==, [], []=) Hash(==, ===, [], []=) String(%, *, +, <<, <=>, ==, =~, [], []=) Numeric(+, -, <=>) Float(+, -, *, /, %, **, ==, <, >, <=, >=, <=>) Integer(+, -, *, /, %, **, ==, <=, >=, <=>, <<, >>, [], &, |, ^, ~, **) Range(==, ===) Regexp(==, ===, =~)
演算子の優先順位と結合方向編集
演算子には優先順位 (結合力の強さ) と結合方向があります。
a + b - c
は
( a + b ) - c
と同じ(加減算は左結合)。
a + b * c
は
a + ( b * c )
と同じ(乗除算は加減算より優先順位が高い)。
a = b = 0
は
a = ( b = 0 )
と同じ(代入は右結合)。
様々なクラス・オブジェクトとリテラル編集
Ruby では、変数に束縛することが出来るものは全てオブジェクトです。 オブジェクトは必ず1つのクラスに属しています。 全てのクラスにリテラル表現があるわけではありませんが、組込みライブラリーの主要なクラスにはリテラル表現が用意されています。
数値クラス編集
数値クラスは整数クラスあるいは浮動小数点数クラスです。
- 数値クラス
p 2 + 6 p 2.0 + 6 p 2 + 6.0 p 2.0 + 6.0 p 2.class p 2.methods p 2.0.class p 2.0.methods
- 表示結果
8 8.0 8.0 8.0 Integer [:anybits?, :nobits?, :downto, :times, :pred, :pow, :**, :<=>, :<<, :>>, :<=, :>=, :==, :===, :next, :lcm, :digits, :[], :~, :gcd, :gcdlcm, :-@, :upto, :%, :chr, :&, :*, :+, :bit_length, :inspect, :-, :/, :integer?, :even?, :odd?, :size, :succ, :<, :>, :ord, :to_int, :to_s, :to_i, :to_f, :to_r, :div, :divmod, :fdiv, :^, :coerce, :modulo, :remainder, :abs, :magnitude, :zero?, :floor, :ceil, :round, :truncate, :numerator, :|, :denominator, :rationalize, :allbits?, :dup, :arg, :real?, :+@, :rect, :polar, :real, :imaginary, :imag, :abs2, :angle, :phase, :conjugate, :rectangular, :to_c, :conj, :infinite?, :finite?, :eql?, :singleton_method_added, :quo, :clone, :i, :nonzero?, :step, :positive?, :negative?, :between?, :clamp, :singleton_class, :itself, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :display, :hash, :public_send, :class, :frozen?, :tap, :then, :yield_self, :extend, :method, :public_method, :singleton_method, :define_singleton_method, :=~, :!~, :nil?, :respond_to?, :freeze, :object_id, :send, :to_enum, :enum_for, :__send__, :!, :__id__, :instance_eval, :instance_exec, :!=, :equal?] Float [:arg, :-@, :**, :<=>, :<=, :>=, :==, :===, :angle, :phase, :positive?, :nan?, :infinite?, :finite?, :next_float, :prev_float, :eql?, :%, :*, :+, :inspect, :-, :/, :<, :>, :to_int, :to_s, :to_i, :to_f, :to_r, :divmod, :fdiv, :quo, :coerce, :modulo, :abs, :magnitude, :zero?, :floor, :ceil, :round, :truncate, :numerator, :denominator, :rationalize, :negative?, :hash, :dup, :real?, :+@, :rect, :polar, :real, :imaginary, :imag, :abs2, :conjugate, :rectangular, :to_c, :conj, :integer?, :singleton_method_added, :div, :clone, :i, :remainder, :nonzero?, :step, :between?, :clamp, :singleton_class, :itself, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :display, :public_send, :class, :frozen?, :tap, :then, :yield_self, :extend, :method, :public_method, :singleton_method, :define_singleton_method, :=~, :!~, :nil?, :respond_to?, :freeze, :object_id, :send, :to_enum, :enum_for, :__send__, :!, :__id__, :instance_eval, :instance_exec, :!=, :equal?]
浮動小数点数リテラルは、小数点以下が 0 でも 6. の様に 0 を省略できません。
文字列クラス編集
文字列リテラルは '
あるいは "
で囲います。
上の例の、'Hello World!'
が文字リテラルの一例です。
- 文字列クラス
p "2" + "6" p "abc" * 6 p "→%d" % 6 p "2".concat "6" p "abc".class p "abc".methods
- 表示結果
"26" "abcabcabcabcabcabc" "→6" "26" String [:unicode_normalized?, :encode!, :unicode_normalize, :ascii_only?, :unicode_normalize!, :to_r, :encode, :to_c, :include?, :%, :*, :+, :unpack, :unpack1, :count, :partition, :+@, :-@, :<=>, :<<, :sum, :==, :===, :next, :=~, :[], :[]=, :empty?, :casecmp, :eql?, :insert, :casecmp?, :match?, :bytesize, :match, :next!, :succ!, :index, :upto, :replace, :rindex, :chr, :clear, :byteslice, :getbyte, :setbyte, :freeze, :scrub, :scrub!, :dump, :inspect, :intern, :upcase, :downcase, :capitalize, :swapcase, :upcase!, :undump, :length, :size, :downcase!, :succ, :swapcase!, :hex, :capitalize!, :split, :chars, :oct, :grapheme_clusters, :concat, :codepoints, :lines, :bytes, :to_str, :end_with?, :start_with?, :reverse, :reverse!, :sub, :to_s, :to_i, :to_f, :rjust, :center, :prepend, :crypt, :ord, :chomp, :strip, :to_sym, :ljust, :delete_prefix, :delete_suffix, :lstrip, :gsub, :scan, :chomp!, :sub!, :gsub!, :rstrip, :delete_prefix!, :chop, :lstrip!, :rstrip!, :chop!, :delete_suffix!, :strip!, :tr_s, :delete, :squeeze, :tr!, :tr, :delete!, :squeeze!, :each_line, :each_byte, :tr_s!, :each_codepoint, :each_grapheme_cluster, :slice, :slice!, :each_char, :encoding, :force_encoding, :b, :valid_encoding?, :rpartition, :hash, :between?, :clamp, :<=, :>=, :<, :>, :singleton_class, :dup, :itself, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :display, :public_send, :class, :frozen?, :tap, :then, :yield_self, :extend, :clone, :method, :public_method, :singleton_method, :define_singleton_method, :!~, :nil?, :respond_to?, :object_id, :send, :to_enum, :enum_for, :__send__, :!, :__id__, :instance_eval, :instance_exec, :!=, :equal?]
文字オブジェクトの +
演算子は、concatメソッドの別名です。
この様に、同じ演算子でも左辺に来るオブジェクトによって行われる処理が変わります。
式展開編集
"
で囲んだ文字列リテラル中には #{ 式 }
の形で式を埋込む事ができます。
'
で囲んだ文字列リテラル中では #{ 式 }
の形で式は展開されません。
- 文字列リテラルに式を埋込んだ例
- 表示結果
- 文字列リテラルに式を埋込んだ例
p "12 + 8 = #{12 + 8}" p %|"abc".upcase = #{"abc".upcase}|
- 表示結果
"12 + 8 = 20" "\"abc\".upcase = ABC"
文字リテラル編集
p ?a p ?字 p ?😈 p ?😈.class
実行結果
"a" "字" "😈" String
正規表現クラスと正規表現リテラル編集
配列クラスと配列式編集
ハッシュクラスとハッシュ式編集
範囲クラスと範囲演算子式編集
範囲演算子式は、範囲クラス(Range)のリテラルで開始 .. 終了
の形式です。
- Rangeオブジェクト
r = (0..9) p r p r.class r.each {|x| p x }
- 実行結果
0..9 Range 0 1 2 3 4 5 6 7 8 9
- putsメソッドはオブジェクトにto_sメソッドで適用した結果を表示するのに対し、pメソッドはオブジェクトにinspectメソッドで適用した結果を表示します。
- putsとp
puts "Hello, world!" p "Hello, world!"
- 実行結果
Hello, world! "Hello, world!"
シンボル編集
%記法編集
メソッド編集
メソッド(method)は、処理の塊に名前を付けて再利用する仕組みです。
一般的なプログラミング言語でのサブルーチン・プロシージャあるいは命令と呼ばれるものと共通する特徴を持ちますが、主語に相当するオブジェクトがあるところが異なります。
メソッドはキーワード def
を使って定義します。
- コード
def hello() puts "Hell World!" end hello() self.hello()
- 表示結果
Hell World! Hell World!
- メソッド
hello
の定義の開始 - メソッド
hello
の本体 - メソッド
hello
の定義の終了 - メソッド
hello
の呼出し - メソッド
hello
をself
を明示して呼出し
仮引数を伴ったメソッド編集
メソッドには、仮引数としてオブジェクトを渡すことができます。
- 仮引数を伴ったメソッド
def show_sum(a, b) puts a + b end show_sum(1, 3) show_sum(5, 2)
- 表示結果
4 7
戻り値を返すメソッド編集
メソッドは、戻り値としてオブジェクトを返すことができます。
- 戻り値を返すメソッドの例
def get_sum(a, b) return a + b end puts get_sum(1, 3) puts get_sum(5, 2)
- 表示結果
4 7
変数のスコープ編集
メソッドの中で作られた変数のスコープはメソッドローカルです。
- 変数のスコープ
a = 100 x = 99 def get_sum(a, b) x = 0 return a + b end puts [a,x] puts get_sum(1, 3) puts get_sum(5, 2)
- 表示結果
100 99 4 7
- 1行目で 100 が代入された変数 a は、メソッド get_sum の仮引数 a とは関係なく 100 のまま
- 2行目で 99 が代入された変数 x は、メソッド get_sum の中で 0 が代入された x とは関係なく 99 のまま
この様に、メソッドの中からはメソッドの外の変数を(仮引数を介すなどの手段を取らない限り)参照出来ません。 また、メソッドをスコープとする変数をメソッドの呼出し元から参照できません。
Rubyと変数宣言 |
「メソッドの中で作られた」と、やや歯切れの悪い表現ですが、Ruby では変数を「宣言」しません。
強いて言うならば、最初の代入が変数の宣言です。 |
メソッドの引数リストを囲む括弧の省略 |
メソッドの引数リストを囲む括弧 p( と ) は、曖昧さのない場合は省略可能です。
上の例は、
|
クラス編集
ユーザー定義クラス編集
都市間の大圏距離編集
Go/メソッドとインターフェースの都市間の大圏距離を求めるメソッドを追加した例を、Rubyに移植しました。
- 都市間の大圏距離
class GeoCoord attr_accessor :longitude, :latitude def initialize(longitude, latitude) @longitude, @latitude = longitude, latitude end def to_s() ew, ns = "東経", "北緯" long, lat = @longitude, @latitude ew, long = "西経", -long if long < 0.0 ns, lat = "南緯", -lat if lat < 0.0 "(#{ew}: #{long}, #{ns}: #{lat})" end def distance(other) i, r = Math::PI / 180, 6371.008 Math.acos(Math.sin(@latitude * i) * Math.sin(other.latitude * i) + Math.cos(@latitude * i) * Math.cos(other.latitude * i) * Math.cos(@longitude * i - other.longitude * i)) * r end end def GeoCoord(longitude, latitude) GeoCoord.new(longitude, latitude) end Sites = { "東京駅": GeoCoord(139.7673068, 35.6809591), "シドニー・オペラハウス": GeoCoord(151.215278, -33.856778), "グリニッジ天文台": GeoCoord(-0.0014, 51.4778), } Sites.each{|name, gc| puts "#{name}: #{gc}" } keys, len = Sites.keys, Sites.size keys.each_with_index{|x, i| y = keys[(i + 1) % len] puts "#{x} - #{y}: #{Sites[x].distance(Sites[y])} [km]" }
- 実行結果
東京駅: (東経: 139.7673068, 北緯: 35.6809591) シドニー・オペラハウス: (東経: 151.215278, 南緯: 33.856778) グリニッジ天文台: (西経: 0.0014, 北緯: 51.4778) 東京駅 - シドニー・オペラハウス: 7823.269299386704 [km] シドニー・オペラハウス - グリニッジ天文台: 16987.2708377249 [km] グリニッジ天文台 - 東京駅: 9560.546566490015 [km]
包含と継承編集
JavaScript/クラス#包含と継承を、Rubyに移植しました。
- 包含と継承
class Point def initialize(x = 0, y = 0) @x, @y = x, y end def inspect() "x:#{@x}, y:#{@y}" end def move(dx = 0, dy = 0) @x, @y = @x + dx, @y + dy self end end class Shape def initialize(x = 0, y = 0) @location = Point.new(x, y) end def inspect() @location.inspect() end def move(x, y) @location.move(x, y) self end end class Rectangle < Shape def initialize(x = 0, y = 0, width = 0, height = 0) super(x, y) @width, @height = width, height end def inspect() "#{super()}, width:#{@width}, height:#{@height}" end end rct = Rectangle.new(12, 32, 100, 50) p ["rct=", rct] p ['rct.instance_of?( Rectangle ) => ', rct.instance_of?(Rectangle)] p ['rct.instance_of?( Shape ) => ', rct.instance_of?(Shape)] p ['rct.instance_of?( Point ) => ', rct.instance_of?(Point)] rct.move(11, 21) p ["rct=", rct]
- 実行結果
["rct=", x:12, y:32, width:100, height:50] ["rct.instance_of?( Rectangle ) => ", true] ["rct.instance_of?( Shape ) => ", false] ["rct.instance_of?( Point ) => ", false] ["rct=", x:23, y:53, width:100, height:50]
制御構造編集
制御構造(せいぎょこうぞう、control flow)とは、「順次」「分岐」「反復」という基本的な処理のことを言います。
Rubyの真理値 |
制御構造は「条件式」が真であるか偽であるかによって分岐や反復の振る舞いが変わります。
では「条件式」が真・偽はどの様に決まるのでしょう? Rubyでは なので |
条件分岐編集
Rubyの条件分岐には、if, until と caseの3つの構文があります。
if編集
ifは条件式によって実行・否を切り替える構造構文で、評価した式の値を返すので条件演算子でもあります。
- ifの例
a = 0.0 / 0.0 if a < 0 puts "minus" elsif a > 0 puts "plus" elsif a == 0 puts "zero" else puts a end puts( if a < 0 "minus" elsif a > 0 "plus" elsif a == 0 "zero" else a end )
- 表示結果
NaN NaN
- elsif節
- ifは、オプショナルな elsif 節を設け、条件式が偽であった時に別の条件に合致した処理を実行させることが出来ます。
- else節
- ifは、オプショナルな else 節を設け、条件式が偽であった時に処理を実行させることが出来ます。
- ifは値を返すので、メソッドの実引数に使うことが出来ますし、代入演算の右辺にも使えます。
後置のif編集
Rubyには、Perlのような後置のifがあります。
- 後置のifの例
n = 0 puts "nは0" if n == 0 puts "nは1" if n == 1
- 表示結果
nは0
unless文編集
unless文(アンレスぶん、unless statement)は条件式によって実行・否を切り替える構造構文ですが、ifとは条件式に対する挙動が逆です。
- unless文の例
a = 0.0 / 0.0 unless a == 0 puts "Non-zero" else puts a end
- 表示結果
Non-zero
- else節
- unless文は、オプショナルな else 節を設け、条件式が真であった時に処理を実行させることが出来ます。
- また、unless文は elsif 節は持てません。
後置のunless編集
Rubyには、Perlのような後置のunlessがあります。
- 後置のunlessの例
n = 0 puts "nは0" unless n == 0 puts "nは1" unless n == 1
- 表示結果
nは1ではない
case編集
caseは、複数の条件式によって処理を降る分ける用途の為に用意されています。
- caseの例
n = 2 case n when 1 puts "one" when 2 puts "two" when 3 puts "three" else puts "other" end
- 表示結果
two
- C言語系のswitch文に慣れた人はbreakがないことに気がつくと思います。Rubyのcaseはfall throughしませんし、fall throughさせる方法もありません。
when節が定数でなく式を受付ける事を使ったトリック編集
ifを使ったコードをcaseに書き換えてみましょう。
- case true トリック
a = 0.0 / 0.0 case true when a < 0 puts "minus" when a > 0 puts "plus" when a == 0 puts "zero" else puts a end
- 表示結果
NaN
このコードは when 節の式の値とcaseの式を ===
で比較し、最初に一致した when に対応する文が実行される事を利用しています。
JavaScriptのswitch文のcase節にも式が使えるので、同じトリックが使えます。
繰返し編集
Rubyには、他のプログラミング言語のような繰返し文と、イテレーターメソッドがあります。
繰返し構文編集
Rubyの繰返し文には、while文 until for文 と loop文 の4つがあります[5]。
while文編集
while文(ホワイルぶん、while statement)は条件が真である間、文を実行しつづけます。
- 構文
while 条件式 [ do ] 文1 文2 : 文n end
do
は省略できます。
- while文のコード例
i = 0 while i < 5 do puts i i += 1 end
- 2行目の
i < 5
が真の間、次の2行を繰返します。 - 4行目の
i += 1
はi = i + 1
の構文糖 - 実行結果
0 1 2 3 4
until編集
until(アンティルぶん、until statement)は条件が偽である間、文を実行しつづけます。while文とは条件に対する挙動が逆です。
- 構文
until 条件式 [ do ] 文1 文2 : 文n end
do
は省略できます。
- untilのコード例
i = 0 until i == 3 do puts i i += 1 end
- 2行目の
i == 3
が偽の間、次の2行を繰返します。 - 実行結果
0 1 2
for文編集
Rubyにもfor文がありますが、多くの言語でfroeach文と呼ばれるもので、C言語系のfor (;;)
とは異なります。
- for文の構文
for 変数 in コレクション 文 end
- コレクションは Range, Array, Hash など内部構造を持つオブジェクトです。
Rangeオブジェクトとfor文編集
Rangeオブジェクトは、整数の区間を表し範囲演算子 開始 ... 終了
で生成します。
- コード
rng = 1..3 puts rng.class for n in rng do puts "テキスト"; end
- 実行結果
Range テキスト テキスト テキスト
Arrayオブジェクトとfor文編集
Arrayオブジェクトは、任意の Ruby オブジェクトを要素として持つことができます。
配列式[要素1, 要素2, … 要素n]
で生成します。
- コード
animals = ["ネコ", "金魚", "ハムスター"] puts animals.class for animal in animals do puts "動物 #{animal}" end
- 実行結果
動物 ネコ 動物 金魚 動物 ハムスター
Hashオブジェクトとfor文編集
Hashオブジェクトは、任意の Ruby オブジェクトをキーに、任意の Ruby オブジェクトを値に持つことができる連想配列です。
Hash式{キー1 => 値1, キー2 => 値2, キーn => 値n}
で生成します。
また、キーが Symbol の場合
Hash式{キー1: 値1, キー2: 値2, キーn: 値n}
で生成することが出来ます。
- コード
animals = {cat: "ネコ", gold_fish: "金魚", hamster: "ハムスター"} puts animals.class for en, animal in animals do puts "動物 #{en}: #{animal}" end
- 実行結果
Hash 動物 cat: ネコ 動物 gold_fish: 金魚 動物 hamster: ハムスター
このように、Rubyのfor文は多様なコレクションを受け付けます。
loop文編集
loop文(ループぶん、loop statement)は永久ループを提供します。
- 構文
until [ do ] 文1 文2 : 文n end
- loop文のコード例
i = 1 loop puts "%b" % i i <<= 1 break if i > 2**80 end
- 実行結果
0b1 0b10 0b100 0b1000 0b10000 0b100000 0b1000000 0b10000000 0b100000000
- 5行目の、
break if i > 2**80
でループを脱出するようにしています。この様に break や return あるいは例外が上がらないとループは永久に終わりません。 - このコードは、Rubyにはない do-while文を模倣する例にもなっています。
イテレーターメソッド編集
eachメソッド編集
上述 for を使った配列の処理と、同じ処理を、次のようにコレクションのeach
メソッドを使っても書けます。
- コード
rng = 1..3 puts rng.class rng.each do puts "テキスト"; end animals = ["ネコ", "金魚", "ハムスター"] animals.each do |animal| puts "ペット:#{animal}" end animals = {cat: "ネコ", gold_fish: "金魚", hamster: "ハムスター"} puts animals.class animals.each do |en, animal| puts "動物 #{en}: #{animal}" end
- 実行結果:
Range テキスト テキスト テキスト ペット:ネコ ペット:金魚 ペット:ハムスター Hash 動物 cat: ネコ 動物 gold_fish: 金魚 動物 hamster: ハムスター
eachメソッドを使ったハック |
普段あまり使うことはありませんが、配列のeachメソッドは戻値を返します。
通常は配列自身ですが、breakでループを中断すると nil を返します。 この特徴を利用すると、ZigやPythonのループと結合したelseの様に「ループを完走したときだけ実行する処理」を記述できます。
|
Integer#times編集
Integer#timesは与えられたブロックをオブジェクトの示す整数値回くりかえします。
- コード
3.times{ puts 'Hello, world!' }
- 実行結果
Hello, world! Hello, world! Hello, world!
- 繰返したい処理が2行以上ある場合
3.times { puts 'Hello' puts 'World' puts '' }
- 実行結果
Hello World Hello World Hello World
- ループ変数を使た例
3.times do |i| puts "#{i}の倍は#{2 * i}" end
- 実行結果
0の倍は0 1の倍は2 2の倍は4
- ブロックを伴わないtimesメソッド
iter = 3.times puts iter.class puts iter.next # 0 puts iter.next # 1 puts iter.next # 2 # puts iter.next # `next': StopIteration: iteration reached an end
- 実行結果
Enumerator 0 1 2
- Integer#times にブロックを渡さないと、Enumeratorオブジェクトが返ります。
- Enumeratorオブジェクトは外部イテレーターと呼ばれnextメソッドで反復を行えます。
lazy編集
イテレーション メソッドでメソッド チェインを使うと、中間結果としてに大きなコレクションができてしまいます。 lazyを使うと、遅延評価を行いこれを避けることができます。 Enumableをプログラミング/ジェネレターに変換していると捉えることができます。
- lazy
print "non lazy: " p (0...10).filter{ |i| print "#{i} " i % 2 == 0 }.map { | i | print "(#{i}) " i }.first(3) print "lazy: " p (0...10).lazy.filter{ |i| print "#{i} " i % 2 == 0 }.map { | i | print "(#{i}) " i }.first(3)
- 実行結果
non lazy: 0 1 2 3 4 5 6 7 8 9 (0) (2) (4) (6) (8) [0, 2, 4] lazy: 0 (0) 1 2 (2) 3 4 (4) [0, 2, 4]
高階関数編集
lambda編集
無名関数を定義するにはKernel.#lambdaを使用します。Proc.newも近い機能を提供しますが、lambda のほうがより厳密で、引数の数が異なるって場合エラーとなります。
lambda {|x, y| x + y }[2, 3] # => 5
lambdaが返すProcオブジェクトにアクセスするにはブラケットを使用します。callメソッドを呼んでも同じことです。
HTTPクライアント編集
open-uriを使用すると簡単ですが、より高度なインタフェースが必要な場合はNet::HTTPを使用します。
require 'open-uri'
puts open('http://www.example.com/').read
open-uriは組み込みのopenメソッド(Kernel.#open)をオーバーライドします。ワンライナーで書くこともできます。
% ruby -ropen-uri -e 'puts open(ARGV.shift).read' http://www.example.com/
ARGVはコマンドライン引数の配列、Array#shiftメソッドは最初の要素を取り出します。