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
    
我以前在ruby中使用过哈希和lambdas,但不是这样。这更像是存储函数的一种方式:

if x < 2
  x
else
 fibonacci[x-1] + fibonacci[x-2]
如果x<2
x
其他的
斐波那契[x-1]+斐波那契[x-2]
  • 你能详细解释一下这是如何工作的吗?这是使用递归吗
  • 像这样的散列和lambdas之间有什么区别

  • 在第一种情况下,您将定义一个递归,该递归将被递归调用

    在散列的情况下,值也将递归计算,但会存储,然后访问以给出结果

    • 兰姆达

      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}
    
    如您所见,前面的计算都缓存在散列中,这意味着对于其他斐波那契数,不必再次执行这些计算