ブロックとprocsによるRubyの関数型プログラミング
19334 ワード
オブジェクト指向プログラミング対機能プログラミング
Rubyは真のオブジェクト指向プログラミング言語です.一般的に、我々はオブジェクト指向(OOP)プログラミングが大好き!オブジェクト指向プログラミングは、私たちのコードを読みやすく、再利用可能な、モジュール化し、一般的にかなり簡単なパラダイムです.
Rubyでは、すべてがオブジェクトです.一般に、クラスとインスタンスにコードを分割するので、これはよく知られていない概念です.本当に、すべて!
それを試してみましょう!
self
# => main
self.class
# => Object
nil.class
# => NilClass
true.class
# => TrueClass
イーブンnil
? はい.nil
はsingleton インスタンスNilClass
.さて、これは何ですか
main
事まあ.main
デフォルトはtop level context Rubyで我々に提供されます.すべてがオブジェクトであるので、我々はこれをグローバルオブジェクトと呼びます.しかし、私たちのグローバルなオブジェクトでさえObject
.実際には、クラス
Object
クラスのインスタンスですClass
!self.class.class
# => Class
我々はクラスのクラスをオーバーライドすることができますし、自分自身を確認!class Class
alias prev_new new
def new(*args)
print "Creating a new #{self.name} class. \n"
prev_new(*args)
end
end
class Text
end
t = Text.new
# => ...
# => Creating a new Text class.
方法は?
我々がこれをするとき、物事は少しトリッキーになります.Rubyメソッドは、コードのスニペットであり、オブジェクトではありません.
関数パラダイム(この場合、typescript)を考慮する他の言語では、関数を変数に割り当てて呼び出します.
function ourFunction():void {
console.log("hello");
}
let variable = ourFunction
variable()
// "hello"
// => undefined
Rubyで同様のアプローチを試してみましょう.def method
puts "hello"
end
variable = method
variable()
# NoMethodError (undefined method `variable' for main:Object)
variable
# => nil
Rubyでvariable
が返り値method
. 我々が呼ぶようにmethod
, それは暗黙のうちに呼び出されます.呼び出しmethod
, このメソッドはmethod()
. 現在のパラダイムでは、関数自体を別の変数に渡すことはできません.高次関数
いくつかの点で、この制限は我々の創造力を妨げるDRY コード.
また、高次の関数を作成する能力を失います.
Higher-order function: a function that takes a function as a parameter or returns a function.
タイプスクリプトの単純な高次の機能を見てみましょう.
function multiplier(number1: number): (number2: number) => number {
return function(number2: number): number {
return number1 * number2
}
}
let doubler = multiplier(2)
doubler(6)
// => 12
Rubyでは、ブロックとprocsのパワーを使用して関数プログラミングのいくつかの原則をエコーすることができます!それを与えましょう!Proc 101
我々がnitty grittyコードに入る前に、休止して、よりよく理解しましょう
procs
. エーProc
コードのブロックをオブジェクトとして格納する特殊なRubyオブジェクトです.我々が我々を例示するようにProc
インスタンスは、メソッドパラメーターの直後にブロックをインスタンス化することができます.print_greetings = Proc.new() do
puts "Welcome!"
puts "Bonjour!"
puts "¡Bienvenidas!"
end
print_greetings
# => #<Proc:0x00007fe0ff042a08>
いくつかの異なる方法でprocを呼び出すことができます..call
.yield
[]
.call
構文的糖で.()
print_greetings.call
print_greetings.yield
print_greetings[]
print_greetings.()
# Welcome!
# Bonjour!
# Bienvenidas!
# => nil
ブロックパラメータ
通常のメソッドと同様に、ブロックはパラメータを受け取ることができます.実際には、いくつかの共通点があります.
*
未決定の量の引数を取得できるようにします.nil
パラメータに代入されますmultiply_by_two = Proc.new do |item|
item * 2
end
変換するProc
ブロックに戻ると、我々は&
演算子の内部の演算子は、ブロックを期待します.以下の2つの例は等価です.[1,2,3,4,5].map do |item|
item * 2
end
[1,2,3,4,5].map(&multiply_by_two)
# => [2, 4, 6, 8, 10]
どのようにクールです!メソッドにブロックを渡す規則をいくつかレビューしましょう..ourFunction(1, 2, &multiply_by_two)
[1,2,3,4,5].map(&multiply_by_two, &multiply_by_three)
はsyntaxerrorを生成する.procsによる高次関数の再生成
上記の例から高次関数を再訪問し、ブロックとprocsの新たな知識を持って再作成しましょう.
def multiplier(number1)
Proc.new {|number2| number1 * number2}
end
doubler = multiplier(2)
# => #<Proc:0x00007fe7719b91b8>
doubler.call(6)
# => 12
収量を理解する
あなたがレールに精通しているならば、あなたはおそらく見えました
yield
キーワード.あなたの部品の注入を許可する.erb
テンプレートの内部.同様に、普通の古いルビーでyield
現在のコードの実行を一時停止し、渡されたブロックにyieldします.def our_method
puts "top of method"
yield
puts "bottom of method"
end
our_method {puts "we are inside the block"}
# top of method
# we are inside the block
# bottom of method
# => nil
ブロックにパラメータを渡すことができます.def our_method_w_parameters
puts "top of method"
yield("karson", "nyc")
puts "bottom of method"
end
our_method_w_parameters do |name, loc|
puts "my name is #{name}, and I live in #{loc}"
end
# top of method
# my name is karson, and I live in nyc
# bottom of method
# => nil
しかし、それが難しいコード化されるとき、再利用できる方法はこのコードです?この属性を使用してインスタンス属性を使用するにはyield
.proc範囲
procsは定義されているスコープ内に存在します.これは、いくつかの誤解を招く可能性があります
self
. 以下の例を見てみましょう.class Person
attr_accessor :name, :loc
def initialize(name, loc)
@name = name
@loc = loc
end
def ex_block
yield
end
end
k = Person.new("karson", "nyc")
# => #<Person:0x00007fded014edb0 @name="karson", @loc="nyc">
k.ex_block {puts self.name, self.loc}
# NoMethodError (undefined method `name' for main:Object)
self
はmain
オブジェクトがグローバルスコープにあることを示します.バインドしたいならself
ブロックが呼ばれるインスタンスには、インスタンス化で定義されなければなりません.class Person
attr_accessor :name, :loc, :instance_proc
def initialize(name, loc)
@name = name
@loc = loc
@instance_proc = Proc.new() do
puts self.name, self.loc
end
end
def ex_proc(&proc)
yield
end
end
k = Person.new("karson", "nyc")
# => #<Person:0x00007ff6aa94c228 @name="karson", @loc="nyc", @instance_proc=#<Proc:0x00007ff6aa94c1d8>>
k.ex_proc(&k.instance_proc)
# karson
# nyc
エンディング
あなたが今知っているものを使う
Procs
and blocks
, 方法を再作成しましょう.map
カスタムクラスでMArray
.class MArray < Array
def initialize(*args)
super(args)
end
def map(&block)
newMArr = MArray.new()
for element in self
newMArr << yield(element)
end
newMArr
end
end
m = MArray.new(1, 2, 3, 4, 5)
# =>[1, 2, 3, 4, 5]
m.map { |item| item * 2 }
# => [2, 4, 6, 8, 10]
結論
procsは、私たちがコードドライを維持し、OOP概念と機能プログラミングの再生機能を実装できるようにする強力なRubyコンセプトです.オフィシャルサイトRuby-Doc procsに関するドキュメントとコメントブローであなたの考えを知らせてください!
Reference
この問題について(ブロックとprocsによるRubyの関数型プログラミング), 我々は、より多くの情報をここで見つけました https://dev.to/karsonkalt/functional-programming-in-ruby-with-blocks-and-procs-362gテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol