Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rubyでの素朴なニル処理

過去6か月ほどの間、私はRustでNESエミュレーターを使用してきました。ご想像のとおり、私はさびについて多くのことを学び、さらにNESの内部についても学びました。しかし、その経験によって、Rubyの見方も変わりました。

具体的には、nilを返すメソッドについて少し妄想的になりました。 。

何かを支持しないと、何にでも落ちる

nilとは Rubyの意味ですか?ほとんど何でも。メソッドがnilを返す場合、それは次のことを意味する可能性があります:

  • メソッドには戻り値がありません
  • 通常は戻り値がありますが、今回はありません
  • データベースからNULLの値を返します
  • 予期しないことが起こりました

これによりコードが読みにくくなり、最も一般的なの主な原因になります。 RubyのRuby例外:NoMethodError 。例外監視サービスの一部所有者として、NoMethodError 私の子供を学校に通わせています。

次のコードを見てください。 nilを返します ほとんどの場合、if ステートメントはnilと評価されます 条件が一致せず、elseがない場合 。

def color_name(rgb)
  if rgb == 0x000000
    "black"
  end
end

color_name("#FFFFFF").titleize
=> NoMethodError: undefined method `titleize' for nil:NilClass

経験豊富なRuby開発者であれば、このうさぎの穴がさらに深くなることをご存知でしょう。 nilのこれらの異なる意味が奇妙な方法で重複することがあり、たとえば、データベースの値がNULLであるかどうかを知ることができなくなります。 、またはデータベースに値がまったくありません。

より良い方法

Rustには、nilのようなものはありません。 。代わりに、関数が値を返す場合と「何も返さない」場合があることを示したい場合は、Optionを使用します。 。

Options 特定の値を含むか、値を含まないタイプです。コードでの表示は次のとおりです。

Option::Some(42); // Wraps the number 42 in an option
Option::None;     // Indicates "no result"

これは、アドホックなnilよりもすでに良く見えています 使用法ですが、それはさらに良くなります。 Rustコンパイラは、Noneを考慮するように強制します 場合。誤って無視することはできません。

match my_option {
  Some(x) => do_something_with_x(x),
  // If you remove the `None` match below, this code
  // won't compile.
  None => do_the_default_thing()  
}

したがって、色の命名例を次のように錆びた形で書くことができます:

fn color_name(rgb: u32) -> Option<String> {
    if rgb == 0x000000 {
      Some("black".to_owned())
    } else {
      None
    }
}

今、私たちは両方のSomeを処理することを余儀なくされています およびNone 条件:

let name = color_name(0xFFFFFF);

let name = match color_name(0xFFFFFF) {
  Some(value) => value,
  None => "unknown".to_owned(),
}

確かにこれは少し冗長で奇妙な見た目ですが、関数が有用な値を返さない場合を無視することはできません。つまり、理解と保守が容易なコードを意味します。

Rubyでのオプションの実装

Rubyが厳密さに欠けているものは、柔軟性で補います。 Optionのようなものを実装してみるのは面白いと思いました Rubyで。

Rubyはインタプリタ言語であるため、コンパイル時エラーを作成することはできません。ただし、誤ったコードが常に発生する可能性があります エッジケースに遭遇したときにのみ例外を発生させるのではなく、例外を発生させます。

まず、2つのクラスを作成しましょう。 Some 読み取り専用の値を保持します。 None 空です。見た目と同じくらいシンプルです。

  class Some
    attr_reader :value
    def initialize(value)
      @value = value
    end
  end

  class None
  end

次に、Optionを作成します Someのいずれかを保持するクラス またはNone 両方にハンドラーを提供する場合にのみ、それらにアクセスできます。

class Option
  def initialize(value)
    @value = value
  end

  def self.some(value)
    self.new(Some.new(value))
  end

  def self.none()
    self.new(None.new)
  end

  def match(some_lambda, none_lambda)
    if @value.is_a?(Some)
      some_lambda.call(@value.value)
    elsif @value.is_a?(None)
      none_lambda.call()
    else
      raise "Option value must be either Some or None"
    end
 end
end

最後に、新しいOptionを使用するように色の例を書き直すことができます。 クラス:

def color_name(rgb)
  if rgb == 0x000000
    Option.some("black")
  else
    Option.none()
  end
end

puts color_name(0x000000).match(
  -> value { value },
  -> { "no match" })

# Prints "black"
結論

私はまだ実際のプロジェクトでこのテクニックを試していません。多くのNoMethodErrorsを確実に防ぐことができると思います それは常に生産に滑り込みます。見た目は少し面倒で、あまりルビーっぽくはありませんが、少し改良すれば、より快適な構文ができると思います。


  1. Rubyでのラムダの使用

    ブロックはRubyの非常に重要な部分であり、ブロックなしで言語を想像するのは難しいです。しかし、ラムダ?ラムダが好きなのは誰ですか?あなたはそれを使わずに何年も行くことができます。まるで過ぎ去った時代の遺物のようです。 ...しかし、それは完全に真実ではありません。ラムダを少し調べてみると、ラムダにはいくつかの興味深いトリックがあります。 この記事では、ラムダの使用法の基本から始めて、さらに興味深い高度な使用法に移ります。したがって、ラムダを毎日使用していて、それらについてすべて知っている場合は、下にスクロールするだけです。 Lambdasについて覚えておくべき主なことは、それらが関数の

  2. Ruby2.6の9つの新機能

    Rubyの新しいバージョンには、新しい機能とパフォーマンスの改善が含まれています。 変更についていきますか? 見てみましょう! 無限の範囲 Ruby 2.5以前のバージョンは、すでに1つの形式の無限範囲をサポートしています( Float ::INFINITY を使用) )、しかしRuby2.6はこれを次のレベルに引き上げます。 新しい無限の範囲 次のようになります: (1..) これは、(1..10)のような終了値がないため、通常の範囲とは異なります。 。 使用例 : [a, b, c].zip(1..) # [[a, 1], [b, 2], [c, 3]] [1,2,3,