Ruby 哈希与Lambdas
我发现两个查找斐波那契数的例子彼此非常接近:Ruby 哈希与Lambdas,ruby,hash,lambda,Ruby,Hash,Lambda,我发现两个查找斐波那契数的例子彼此非常接近: 兰姆达 fibonacci = ->(x){ x < 2 ? x : fibonacci[x-1] + fibonacci[x-2] } fibonacci[6] # => 8 fibonacci = ->(x){ x < 2 ? x : fibonacci[x-1] + fibonacci[x-2] } fibonacci[6] fibonacci # => <Proc:0x2d026a0@(irb)
- 兰姆达
fibonacci = ->(x){ x < 2 ? x : fibonacci[x-1] + fibonacci[x-2] } fibonacci[6] # => 8
fibonacci = ->(x){ x < 2 ? x : fibonacci[x-1] + fibonacci[x-2] } fibonacci[6] fibonacci # => <Proc:0x2d026a0@(irb):5 (lambda)>
fibonacci=->(x){x<2?x:fibonacci[x-1]+fibonacci[x-2]} 斐波那契[6]#=>8
- 散列
fibonacci = Hash.new{ |h,x| h[x] = x < 2 ? x : h[x-1] + h[x-2] } fibonacci[6] # => 8
fibonacci = Hash.new{ |h,x| h[x] = x < 2 ? x : h[x-1] + h[x-2] } fibonacci[6] fibonacci # => {1=>1, 0=>0, 2=>1, 3=>2, 4=>3, 5=>5, 6=>8}
fibonacci=Hash.new{h,x | h[x]=x<2?x:h[x-1]+h[x-2]} 斐波那契[6]#=>8
if x < 2
x
else
fibonacci[x-1] + fibonacci[x-2]
如果x<2
x
其他的
斐波那契[x-1]+斐波那契[x-2]
在第一种情况下,您将定义一个递归,该递归将被递归调用 在散列的情况下,值也将递归计算,但会存储,然后访问以给出结果
- 兰姆达
fibonacci = ->(x){ x < 2 ? x : fibonacci[x-1] + fibonacci[x-2] } fibonacci[6] # => 8
fibonacci = ->(x){ x < 2 ? x : fibonacci[x-1] + fibonacci[x-2] } fibonacci[6] fibonacci # => <Proc:0x2d026a0@(irb):5 (lambda)>
fibonacci=->(x){x<2?x:fibonacci[x-1]+fibonacci[x-2]} 斐波那契[6] 斐波那契#=>
- 散列
fibonacci = Hash.new{ |h,x| h[x] = x < 2 ? x : h[x-1] + h[x-2] } fibonacci[6] # => 8
fibonacci = Hash.new{ |h,x| h[x] = x < 2 ? x : h[x-1] + h[x-2] } fibonacci[6] fibonacci # => {1=>1, 0=>0, 2=>1, 3=>2, 4=>3, 5=>5, 6=>8}
fibonacci=Hash.new{h,x | h[x]=x<2?x:h[x-1]+h[x-2]} 斐波那契[6] 斐波那契#=>{1=>1,0=>0,2=>1,3=>2,4=>3,5=>5,6=>8}
如果您需要再次访问fibonacci[6],lambda将重新计算结果,而散列将立即给出结果,而无需重新进行计算。是,它使用递归。如果我们看一下{}括号中的代码,我们就能找到答案。让我们开始看散列。new关键字后面的值是默认值。如果哈希中不存在该值,则将分配的值
hash = Hash.new
p hash['new_value'] #=> nil
default_value_hash = Hash.new(0)
puts default_value_hash['new_value'] #=> 0
hash_with_block = Hash.new{|h,x| x}
puts hash_with_block['new_value'] #=> 'new_value'
所以当我们宣布
fibonacci = Hash.new{ |h,x| h[x] = x < 2 ? x : h[x-1] + h[x-2] }
fibonacci=Hash.new{h,x | h[x]=x<2?x:h[x-1]+h[x-2]}
我们基本上是说-创建一个具有默认值的新哈希。如果我们要求一个小于或等于2的数字(x),只需返回输入(x)。否则,给出字典值的总和,其中键为x-1和x-2。基本上是斐波那契算法。如果x-1和x-2不存在,它将再次运行相同的代码,直到两个基本输入值分别为1和2
这两种方法的区别在于散列保存值(在散列中…)。在某些情况下,这可能是一个巨大的优势。每次调用lambda时,它都需要重新计算低于被调用值的所有数字的值
# Let's create a counter to keep track of the number of time the lambda is called.
# Please do not use global variables in real code. I am just lazy here.
@lambda_counter = 0
fibonacci_lambda = ->(x){
@lambda_counter += 1
x < 2 ? x : fibonacci_lambda[x-1] + fibonacci_lambda[x-2]
}
p (1..20).map{|x| fibonacci_lambda[x]}
# => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
p @lambda_counter # => 57290
# lambda called 57290 times!
@hash_counter = 0
fibonacci_hash = Hash.new{ |h,x|
@hash_counter += 1
h[x] = x < 2 ? x : h[x-1] + h[x-2]
}
p (1..20).map{|x| fibonacci_hash[x]}
# => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
p @hash_counter # => 21
# Only called 21 times!
#让我们创建一个计数器来跟踪lambda被调用的时间。
#请不要在实际代码中使用全局变量。我只是懒在这里。
@lambda_计数器=0
斐波那契λ=->(x){
@lambda_计数器+=1
x<2?x:fibonacci_lambda[x-1]+fibonacci_lambda[x-2]
}
p(1..20).map{x | fibonacci_lambda[x]}
# => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
p@lambda#u计数器#=>57290
#lambda打了57290次电话!
@哈希_计数器=0
fibonacci_hash=hash.new{h,x|
@哈希_计数器+=1
h[x]=x<2?x:h[x-1]+h[x-2]
}
p(1..20).map{x | fibonacci_hash[x]}
# => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
p@hash#u计数器#=>21
#只打了21次电话!
调用之间存在巨大差异的原因是递归的本质。lambda不存储其值,当计算10的值时,它将重新计算该值3次超过20次。在散列中,可以存储该值并保存以备将来使用
像这样的散列和lambdas之间有什么区别
lambda和hash没有任何共同之处。你的问题就像在问:
方法和数组之间有什么区别
只是哈希可以为不存在的键指定默认值:
h = Hash.new(10)
h["a"] = 2
puts h["a"]
puts h["b"]
--output:--
2
10
散列还提供了一种动态指定默认值的方法:您可以提供一个块。以下是一个例子:
h = Hash.new do |h, key|
h[key] = key.length
end
puts h['hello']
puts h['hi']
p h
--output:--
5
2
{"hello"=>5, "hi"=>2}
当您访问一个不存在的密钥时,将调用该块,该块可以执行任何您想要的操作。所以有人聪明地发现,可以创建一个散列并指定一个默认值来计算斐波那契数。以下是它的工作原理:
h = Hash.new do |h, key|
if key < 2
h[key] = key
else
h[key] = h[key-1] + h[key-2]
end
end
…3是不存在的密钥,因此使用args h和3调用该块。执行块中的else子句,它将为您提供:
h[3-1] + h[3-2]
或:
但要计算该语句,ruby必须首先计算h[2]。但是当ruby在散列中查找h[2]时,键2是一个不存在的键,因此使用参数h和2调用该块,给出:
(h[2-1] + h[2-2]) + h[1]
(1 + 0) + 1
或:
要计算该语句,ruby首先必须计算第一个h[1],当ruby试图在散列中查找h[1]时,1是一个不存在的键,因此使用参数h和1调用该块。这次执行if分支,导致发生以下情况:
h[1] = 1
1作为h[1]的值返回,给出:
(1 + h[0]) + h[1]
(1 + 0) + h[1]
然后ruby查找h[0],因为0是一个不存在的键,所以使用args h和0调用该块,执行if子句并执行以下操作:
h[0] = 0
0作为h[0]的值返回,给出:
(1 + h[0]) + h[1]
(1 + 0) + h[1]
然后ruby在散列中查找h[1],这一次键1存在,它的值为1,给出:
(h[2-1] + h[2-2]) + h[1]
(1 + 0) + 1
这等于2,所以h[3]设为2。调用h[3]后,您将得到以下输出:
puts h[3]
p h
--output:--
2
{1=>1, 0=>0, 2=>1, 3=>2}
如您所见,前面的计算都缓存在散列中,这意味着对于其他斐波那契数,不必再次执行这些计算