アルペンスキー:ひるがのレーシング振り返り

レッスン受けてきたので、今日の学びを振り返っっておく。

レッスン概要

項目 内容
場所 ひるがの高原スキー場
レーシング名 井上春樹レーシングアカデミー
時間 9:30~11:30(8:00~8:30受付)※午後の部は13:00~15:00
リフト代 4000円(別途ICチップ補償金500円。返金あり。
レッスン料 3500円※1日は6000円
レッスン方法 スタート前の無線とコース整備中に指導
事前予約 あり。上のリンク先(当日枠もあるけど事前予約が望ましい)
備考 基本2本滑ってデラがけ、ビデオなし

振り返り

今日の学びは下記2点。大事なのは、動作とタイミング

  • 動作
    ターン切り替え時に上に抜ける癖があるので、上に抜けないために下記を意識する。

    ターン切り替え時(ターン後半)に内足内足首を織り込んで内足に乗り込み、内足の足裏に荷重がかかっていることを感じながらフォールライン方向に落としていく。

    内腰の落とす方向はフォールライン方向に落としていく。この時、身体が板と同じ方向に向けることを意識する。先に回ってしまうと次ターンの外荷重が抜けてしまう。

  • タイミング
    次ポールの真上くらいまでは、ターンを継続するイメージ。

まとめると下記のようなイメージで滑る。

line.png

チェリー本:パターンマッチ

パターンマッチの基本

rubyのパターンマッチの大きな特徴は下記の3つ。

  1. 配列やハッシュの構造をパターン化して条件分岐させる
  2. in節で=を使わずにローカル変数の宣言と代入が行われる
  3. 独自の変数スコープ(有効範囲)を作らない

上の1.については、パターンマッチは下記のような構文になっている。

casein パターン1
  パターン1にマッチした時の処理
in パターン2
  パターン2にマッチした時の処理
in パターン3
  パターン3にマッチした時の処理
end

見た目はcase文っぽいけど、inの部分が違う。条件分岐という特徴からも、パターンマッチもcase式の一種ということだと思う。 ちなみに本の中では、case/when式で書かれたものをcase文と呼び、case/in式で書かれたものをパターンマッチと呼ぶ。

上の2.については、下記のコードを見るとイメージがつく。

# 配列の場合
case [1, 2, 3]
in [a, b, c]
  puts "#{a} #{b} #{c}"
end
#=> 1 2 3

# ハッシュで値を代入するローカル変数を明示的に指定する場合
case {name: 'Prius', engine: '98ps', motor: '72ps'}
in  {name: vehicle_name, engine: vehicle_engine, motor: vehicle_motor}
  puts "#{vehicle_name} #{vehicle_engine} #{vehicle_motor}"
end
#=> Prius 98ps 72ps

# ハッシュで値を省略する場合
case {name: 'Prius', engine: '98ps', motor: '72ps'}
in  {name: , engine: , motor: }
  puts "#{name} #{engine} #{motor}"
end
#=> Prius 98ps 72ps

パターンマッチの利用パターン

パターンマッチを利用するパターンは下記の7パターン。

valueパターン

valueパターンの特徴は、in節に数値や文字列を直接指定できるという点。case節の式とin節の値が等しければ実行される。

country = 'italy'

case country
in 'japan'
  'こんにちは'
in 'italy' then 'Ciao' # thenを使えば一行で書ける
end
#=> "Ciao"

message = # case文と同様に戻り値を返すので変数への代入もできる。
  case country
  in 'japan'
    'こんにちは'
  in 'italy'
    'Ciao'
  end
message #=> "Ciao"

variableパターン

variableパターンの特徴は、in節のパターンに変数を書いて、ローカル変数の宣言と代入を同時に行うという点。『パターンマッチの基本』の2.の内容にあたる。事前に定義した変数などをin節で使用できる。その際は、^を使う。

alise, name = 'Alise', 'Alise'

case name
in ^alise # in 'Alise'と書いたのと同じ
  'Hey, Alise'
end

arrayパターン

arrayパターンの特徴は、in節に[]を使って配列の構造パターンを指定するという点。『パターンマッチの基本』の2.の配列の内容にあたる。入れ子関係になっていてもOK。

case [1, [2, 3]]
in [a, b]
  "a=#{a}, b=#{b}"
end
#=> "a=1, b=[2, 3]"

case [1, 2, 3]
in [a, *rest] # *を使って、任意の長さを指定できる。
  "a=#{a}, rest=#{rest}"
end
#=> "a=1, b=[2, 3]"

hashパターン

hashパターンの特徴は、in節に{}を使ってハッシュの構造パターンを指定するという点。値に変数を指定すると、その変数に対応する値が格納される。キーの順番はパターンマッチに影響しない。

hashパターンは、ハッシュの各要素がin節で指定したパターンに部分位置していればマッチしたとみなす。よってinの順番に気をつけないと、永久に実施されないメソッドができてしまう。ただ、in節に{}を書いた場合は、『空のハッシュに完全一致』がマッチの条件になる。

基本的には、『パターンマッチの基本』の2.のhashの内容にあたる。 **をつけることで、残りカスをまとめて変数に入れられる。

case{name: 'Alise', age: 20, gender: :female}
in  {name: 'Alise', **rest}
  "rest=#{rest}"
end
#=> "rest={:age=>20, :gender=>:female}"

asパターン

asパターンの特徴は、パターンマッチでマッチしたオブジェクトを変数に代入するという点。=> 変数名と書いてあげると、マッチしたオブジェクトを代入できる。

case {name: 'Alise', age: 20, gender: :female}
in {name: String => name, age: 18.. => age}
  "name=#{name}, age=#{age}"
end
#=> "name=Alise, age=20"

alternativeパターン

alternativeパターンの特徴は、2つ以上のパターンを指定し、どれか1つにマッチすればマッチしたとみなす点。パターンを|で連結する。

case 2
in 0 | 1 | 2
  'matched'
end
#=> "matched"

# _を使うことで、オブジェクトの構造をマッチさせられる。変数_への参照はしない。
case [2021, 4, 1]
in [_,_] | [_, _, _]# 配列の要素が2または、3つならマッチ
 'matched'
end
#=> "matched"

findパターン

findパターンの特徴は、ある範囲を指定して、マッチするものを探すという点。

case[13, 11, 9, 6, 12, 10, 15, 5, 7, 14]
in [*, 10.. => a, 10..=> b, 10..=> c,*]
  "a=#{a} b=#{b} c=#{c}"
end
#=> "a=12 b=10 c=15"

チェリー本:例外処理の概要

例外処理の書き方

begin
  # 例外が起きうる処理
rescue
  # 例外が発生した場合の処理
  retry # 処理をやり直す場合に記述する。
        # begin説の頭からリトライする。通常リトライ回数の制限などを入れる
else
  # 例外が発生しなかった場合の処理※begin説に入れてしまうことが多い
ensure
  # 例外の有無に関わらず実行する処理
end

rescueの部分をrescue 捕捉したい例外クラスにしてあげると、捕捉する例外を限定することもできる。

rescure節は複数書くことができるが、実行されないrescue節ができないように、例外クラスのスーパークラスよりもサブクラスを先に捕捉する順番でコードを記述する。

意図的に例外を発生させる方法

意図的に例外を発生させたいときは、raiseメソッドを用いる。下記のように、原因を特定しやすいようなエラーメッセージをつけておく。

def currency_of(country)
  case country
  when :japan
    'yen'
  when :us
    'dollar'
  else
    raise "無効な国名です。#{country}" # 例外クラスを渡さない場合は、RunTimeError
    # raise ArgumentError, "無効な国名です。#{country}" # 例外クラスを渡すこともできる
  end
end

例外処理のベストプラクティス

例外処理は『ここぞ!』という時にのみ使う機能で、やみくもにrescueや、捕捉すべき出ない例外を捕捉しない。上記を守らないとかえって問題の多いプログラムになってしまうとのこと。

なので、例外発生が予想できる処理であれば、基本的には例外処理よりも条件分岐で対応できるようにする。

予期しないものは、上のコードのように例外を発生させて、エラーメッセージとバックトレースを残して原因調査ができるようにする。

チェリー本:モジュールの理解

モジュールとクラスの違い

  • モジュールからインスタンスの作成はできない
  • 他のモジュールやクラスを継承できない

モジュールの使いどころ

モジュールの用途は下記4つが紹介されていた。以下の用途を、複数使用した場合もよくあるらしい。

  • 継承を使わずにクラスにインスタンスメソッドを追加する、もしくは上書きする
  • 複数のクラスに対して共通の特異メソッド(クラスメソッド)を追加する
  • クラス名や定数名の衝突を防ぐための名前空間を作る
  • 関数的メソッドを定義する
  • 状態を保持する

モジュールの定義

モジュール定義の構文は下記。

module モジュール名
  # モジュール定義(メソッドや定数等)
end

モジュールを利用したメソッド定義(include,extend)

『is-a』の関係が成り立っていない場合、継承は使うべきでない。そんな時に共通のメソッドを持たせたいというような時にモジュールを利用できる。モジュールでメソッドを定義する時には、includeextendを使用する。

インスタンスメソッドとして、モジュールをクラスに追加したい時は、includeでモジュールをクラスに追加する。この追加のことをミックスインという。 module.png

各クラスへのincludeは、本の中の下記図がとてもイメージしやすくて理解の助けになると感じた。 module_image.png

クラスメソッドとしてモジュールをクラスに追加したいときは、extendを使う。 また、includeの場合は、当該クラスから生成されるインスタンス全てに影響してしまうけど、特定のオブジェクトだけにメソッドを追加したいというような時には、extendを使うとインスタンスに特異メソッドとして追加できる。 extend.png

クラスとインスタンスに対してextendを用いるイメージは、多分こんな感じだと思う。 extend

名前空間の作成

モジュールを使って名前空間を設定できる。クラス名などが衝突するような時に、利用してあげると良い。 module_namespace.png

また、クラス定義やモジュール定義を保存するファイルパスは、慣習として名前空間ディレクトリ名、クラス名やモジュール名をファイル名に対応させるらしい。

関数や定数を提供するモジュール

『他のクラスに組み込まずにモジュールから直接メソッドを呼び出したい』という時には、モジュール自身に特異メソッドを定義して、モジュール名.メソッド名とすればメソッドを呼び出すことができる。

また、ミックスインとしても利用したい時には、対象のメソッドを特異メソッドとして定義した後にmodule_functionメソッドで指定してあげれば良い。 ミックスインとしてもモジュールから直接呼び出せるメソッドのことをモジュール関数と呼ぶ。使い方例は下記。

module_function.png

状態を保持するモジュール

クラスインスタンス変数を使って、モジュール自身に変数を変数を保持させることができる。インスタンスを作って何かを操作するということがないのであれば、モジュールにしておいた方がよい。理由は変な勘違いを起こさせないため。具体的にどんな時かというと、外部ライブラリ(gem)だと、ライブラリを実行するための設定値をモジュール自身に持たせたりしている。 使い方例は下記。 module_config.png

チェリー本:色々な変数

ここまでにローカル変数やインスタンス変数などには触れてきたが、Rubyには色んな種類の変数がある。紹介されていた変数を下記にまとめる。

変数名 定義方法 ポイント
グローバル変数 $付き変数 プログラムのどこからでも参照、代入可能
正規表現で用いる$1など予約語として決められているものもある。それらを『組み込み変数』、『特殊変数』とかと呼ぶ
クラス変数 @@付き変数 クラス、サブクラスから同一の変数として呼び出し可能。変更すると関係するクラス全部に影響するので注意
クラスインスタンス変数 クラス構文直下またはクラスメソッド内部に@付き変数 インスタンスメソッドからは参照できず、クラスメソッドからしか呼び出せない。
インスタンス変数 インスタンスメソッド内で@付き変数 インスタンスメソッド内で共有される

各変数のイメージは下記。クラス変数は関係するクラスに1つで、クラスインスタンス変数は関係するクラスの数、インスタンス変数は関係するインスタンスの数だけある感じかな🧐

varias_val.png

チェリー本:メソッドの可視性

他の言語とかでも可視性の話はあるけれど、Rubyでの特徴は下記ということらしい。 publicの場合は当該インスタンスのものは、どこからでも呼び出しできる。public以外のものは、クラス内もしくはサブクラス内からしか呼出できない。

protectedprivateの違いは、同じクラスもしくはサブクラスのインスタンス間の可視性の部分かなと思った。 protectedは、同じクラスもしくはサブクラスなら呼び出しできて、privateは名前の通り自インスタンスからのみ呼び出せる。

上記の内容のイメージは下記のような感じだと思った😅

項目 クラス外参照 同一クラスのインスタンス間参照
public ⭕️ ⭕️
protected ⭕️
private

access.png

実際に色々試してみたのが下記。理解したような結果になっているので、多分正しいはず。

class User
  attr_reader :name

  def initialize(name, weight)
    @name = name
    @weight = weight
  end

  def heavier_than_protected?(other_user)
    other_user.weight_protected < @weight
  end

  def heavier_than_private?(other_user)
    other_user.weight_private < @weight
  end

  protected

  def weight_protected
    @weight
  end

  private

  def weight_private
    @weight
  end
end

alice = User.new('Alice', 50)
bob = User.new('Bob', 60)

p alice.heavier_than_protected?(bob) #=> false
p alice.heavier_than_private?(bob)   #=> error private method called

チェリー本:インスタンスメソッドとクラスメソッド

クラス内で特に意識せずにメソッドを定義すると、それはインスタンスメソッドになる。インスタンスメソッドは各インスタンスに含まれるデータを読み書きするような場合に定義する。 一方、クラスに関係は深いが、各インスタンスのデータを使わないメソッドを定義したい場合には、self.をつけてクラスメソッドを定義する。

具体的な使い方例は、下記が紹介されていた。

class User
  def initialize(name)
    @name = name
  end

  # .selfをつけてクラスメソッドを定義
  def self.create_users(names)
    names.map do |name|
      User.new(name)
    end
  end

  # インスタンスメソッドを定義
  def hello
    "Hello, I am #{@name}."
  end
end

names = ['Alice', 'Bob']
users = User.create_users(names)
users.each do |user|
  puts user.hello
end
# => Hello, I am Alice.
# => Hello, I am Bob.