D-26.Block、Proc、lambda && Valid Perfect Square

Block说:我让Ruby发光发亮。

Ruby中少数天生不属於物件的存在。
未物件化前,只能依附在有设计好的方法後面才有作用。

2.7.3 :064 > 10.to_s {|string| string.to_i}
 => "10"
#後方想再to_i也没用。

block长什麽样子

程序码区块指的是do...end,或使用{ }包起来的code,其内必须有正确格式的程序码。

2.7.3 :137 > [1, 2, 3].map do |num|
2.7.3 :138 >   num ** num
2.7.3 :139 > end
 => [1, 4, 27]
#需要使用较复杂的程序码时用do...end多。

2.7.3 :124 > [1, 2, 3].map {|num| num.to_s}
 => ["1", "2", "3"]
#通常使用较简短的程序码时用{}多。

2.7.3 :308 > {}.class
 => Hash
#只有{}就只是Hash。

哪个好看用哪个...


是map或each才会用到block吗?

不是的,是each与map设计成可以利用bloack。

为你自己学Ruby on Rails
Block 通常得像寄生虫一样依附或寄生在其它的方法或物件(或是使用某些类别把它物件化)


怎麽用?

如果已经接触过Rails专案,应该都知道views/layouts/application.html.erb中,有一个短短的程序码yield,在官方手册里是这样说的。

RailsGuides
Within the context of a layout, yield identifies a section where content from the view should be inserted. The simplest way to use this is to have a single yield, into which the entire contents of the view currently being rendered is inserted:(Rails会把绘制好的版面显示在yield标示的地方。还是中文简洁有力)

<html>
  <head>
  </head>
  <body>
  <%= yield %>
  </body>
</html>

利用yield是一种方式。

2.7.3 :188 > def i_am_not_each(num)
2.7.3 :189 >   yield
2.7.3 :190 >   num.to_s
2.7.3 :191 > end
 => :i_am_not_each
2.7.3 :192 > i_am_not_each(20) {
2.7.3 :193 >   puts "Hello你好吗?,确定你有yield吗?"
2.7.3 :194 > }
Hello你好吗?,确定你有yield吗?
 => "20"

#改写一下
2.7.3 :010 > def i_am_not_each
2.7.3 :011 >   yield
2.7.3 :012 >   self.to_s
2.7.3 :013 > end
 => :i_am_not_each
2.7.3 :014 > 20.i_am_not_each{puts "Hello你好吗?,确定你有yield吗?"}
Hello你好吗?,确定你有yield吗?
 => "20"
#self,之後会更仔细说明。

yield,方法内有遇到他的先绕到block内就对了。

2.7.3 :070 > def little
2.7.3 :071 >   yield
2.7.3 :072 >   puts "我跳进来了"
2.7.3 :073 >   yield
2.7.3 :074 >   puts "我又跳进来了"
2.7.3 :075 > end
 => :little
2.7.3 :076 > little() { puts "我跑出去了" }
我跑出去了
我跳进来了
我跑出去了
我又跳进来了

Proc是一个Class

还有一个就是物件化。

2.7.3 :195 > say_something = Proc.new {puts "yield被藏起来了"}
 => #<Proc:0x00007fa17b3cdd08 (irb):195>
#say_something是变数
#Proc大写又接new方法,那一定是个类别。
2.7.3 :042 > Proc.is_a? Class
=> true

#完成後我们就可以执行
2.7.3 :197 > say_something.call
yield被藏起来了
 => nil

#其他呼叫方法
2.7.3 :208 > say_something.()
yield被藏起来了
 => nil
2.7.3 :209 > say_something[]
yield被藏起来了
 => nil
2.7.3 :210 > say_something === 1
yield被藏起来了
 => nil
2.7.3 :212 > say_something.yield
yield被藏起来了
 => nil

带参数

上面那几种使用方法,感觉上就是为了可以有参数设定的。

2.7.3 :213 > do_some_thing = Proc.new {|string| string.sum }
 => #<Proc:0x00007fa17b3e7578 (irb):213>
2.7.3 :216 > do_some_thing.("你的银行卡密码")
 => 3776
2.7.3 :217 > do_some_thing["你的健保卡密码"]
 => 3843
2.7.3 :218 > do_some_thing === "健保卡没有密码"
 => 3826

理所当然的,要带参数,block里也需要设计参数的位置。

{ |变数| 变数真的是很伟大 }

| |这个符号内写入变数,作为从指向从外部带进的参数用,也当然变数(参数)可以不只设定一个。

2.7.3 :219 > default_variable = Proc.new {|var_one, var_two = 20| puts "你叫#{var_one},永远#{var_two}岁" }
 => #<Proc:0x00007fa17b385198 (irb):219>
2.7.3 :220 > default_variable.("小明", 50)
你叫小明,永远50岁
 => nil
2.7.3 :221 > default_variable.("小明")
你叫小明,永远20岁
 => nil

一样可以预设参数的值。

block内的变数,有效范围只在block里。

2.7.3 :224 > num = 50
 => 50
2.7.3 :225 > variable_test = Proc.new {|int| int.to_s}
 => #<Proc:0x00007fa17b2d65f8 (irb):225>
2.7.3 :226 > variable_test.(num)
 => "50"
2.7.3 :227 > variable_test.(int)
#NameError (undefined local variable or method `int' for main:Object)
2.7.3 :228 > puts int
#NameError (undefined local variable or method `int' for main:Object)
2.7.3 :229 > puts num
50
 => nil

研究一下回传值

一切东西都有值,回传nil是没有回传值,因为nil是nil。

2.7.3 :262 > [1, 2, 3].each {|num| num.to_s}
 => [1, 2, 3]
#[1, 2, 3]是black的回传值吗?

Ruby API
When a block given, passes each successive array element to the block; returns self。each丢元素给block,回传"使用each的物件"。

2.7.3 :263 > [1, 2, 3].each {|num| puts num.to_s}
1
2
3
 => [1, 2, 3]
2.7.3 :264 > [1, 2, 3].each {|num| p num.to_s}
"1"
"2"
"3"
 => [1, 2, 3]

each将资料传进block内,让block内程序码操作,但最後回传值不是取决於block,而是each。
那block到底有没有回传值?

#这时改看map,会更了解。
2.7.3 :265 > [1, 2, 3].map {|num| p num.to_s}
"1"
"2"
"3"
 => ["1", "2", "3"]
2.7.3 :266 > [1, 2, 3].map {|num| num.to_s}
 => ["1", "2", "3"]

有的,map收走了,并集合成新的物件回传。

Calls the block, if given, with each element of self; returns a new Array whose elements are the return values from the block。


但是不能乱加return。

2.7.3 :267 > def i_am_not_each(num)
2.7.3 :268 >   yield
2.7.3 :269 >   num.to_s
2.7.3 :270 > end
 => :i_am_not_each
2.7.3 :271 > i_am_not_each(20) { p "Hello你好吗?,确定你有yield吗?"}
"Hello你好吗?,确定你有yield吗?"
 => "20"
 
2.7.3 :272 > i_am_not_each(20) { return "Hello你好吗?,确定你有yield吗?"}
LocalJumpError (unexpected return) #跳失败了
#yield部分。

2.7.3 :273 > do_some_thing = Proc.new {|string| p string.sum }
 => #<Proc:0x00007fa17b325608 (irb):273>
2.7.3 :274 > do_some_thing.("试试看")
1660
 => 1660
 
2.7.3 :275 > do_some_thing = Proc.new {|string|  string.sum; return "2000" }
 => #<Proc:0x00007fa17b278de0 (irb):275>
2.7.3 :276 > do_some_thing.("试试看")
LocalJumpError (unexpected return)
#物件化也一样,区块内强制加return会出错。

Proc内return不能乱加,但如果就是想要加return?


lambda half life!!??

写法

2.7.3 :309 > new_lambda = lambda {|num|num+1}
 => #<Proc:0x00007fa17b3c6580 (irb):309 (lambda)>

#另一种写法
2.7.3 :310 > another_lambda = ->(num){num+1}
 => #<Proc:0x00007fa17b3ed388 (irb):310 (lambda)>

#补充proc另一种写法
2.7.3 :311 > new_proc = proc {|x|x+1}
 => #<Proc:0x00007fa17b3c5040 (irb):311>

#但是没有Lambda.new, why?

请注意小写。

#刚刚出现过的
2.7.3 :042 > Proc.is_a? Class
 => true
2.7.3 :304 > Proc.is_a? Object
 => true
2.7.3 :300 > x = Proc.new {}
 => #<Proc:0x00007fa17b29f0d0 (irb):300>
2.7.3 :301 > x.class
 => Proc

2.7.3 :293 > Lambda.is_a? Object
NameError (uninitialized constant Lambda)
#Lambda不是一种类别,lambda是一个方法

有请手册

lambda { |...| block } → a_proc
Equivalent to Proc.new, except the resulting Proc objects check the number of parameters passed when called.

再看一下程序码

2.7.3 :312 > x = Proc.new {}
 => #<Proc:0x00007fa17b30a470 (irb):312>
2.7.3 :313 > x.lambda?
 => false
2.7.3 :315 > new_lambda = lambda {|num|num+1}
 => #<Proc:0x00007fa17b3d6f98 (irb):315 (lambda)>
2.7.3 :316 > new_lambda.class
 => Proc
2.7.3 :317 > new_lambda.lambda?
 => true

接着看实例

def proc_test
  new_proc = Proc.new {return 10}
  int= new_proc.call
  int.to_s
end
 => :proc_test
2.7.3 :325 > proc_test()
 => 10
#这里并不是Proc可以加return了,是proc的return让整个程序码回传10就停了,所以结果给10。

def lambda_test
  new_lambda = lambda{ return 10}
  int = new_lambda.call
  int.to_s
end
 => :lambda_test
2.7.3 :331 > lambda_test()
 => "10"
#用lambda形成的proc才让程序码正确运行。

lanbda像是一种故意要与Proc分开,要形成有另一种特性的Proc。
手册里的Proc类别,只有lanbda?方法,但没有lambda方法。

block,Proc,lambda是Ruby的经典面试题,如果能深入讨论Proc与lambda的关系那真的很厉害(大神们常常让菜鸟我找不到下巴),菜鸟我的文章只说明了皮毛。


第四天的leetcode367. Valid Perfect Square
题目连结:https://leetcode.com/problems/valid-perfect-square/
题目重点:不要引入函式库,求是不是正整数的平方根,题目很善良,num去掉负数了。

2.7.3 :332 > Math.sqrt(16)
 => 4.0
#不给用这种解法

整理一下。
可能大大们都清楚,下面被标记的那两行,代表leetcod对给的资料或是解法都有加工处理,有些解法开发工具上可以,但是leetcode上不行,如果发现这样,代表要仔细再看看题目了。

# @param {Integer} num
# @return {Boolean}
def is_perfect_square(num)

end

puts is_perfect_square(16)  #=> true
puts is_perfect_square(14)  #=> false
def is_perfect_square(num)
  #平方 == 有两个相同数字相乘
  #两个连续正整数平方後,中间的数值都不是完美平方
  #那就试到平方 >= num後,停下来判断。
end

def is_perfect_square(num)
  for int in 0..num
    return int * int == num if int * int >= num
  end
end
#很简单对不对?

但是有更简单的

def is_perfect_square(num)
  ans = num ** 0.5
  ans.to_i == ans
end

#原理
2.7.3 :333 > 16 ** 0.5
 => 4.0
2.7.3 :334 > 14 ** 0.5
 => 3.7416573867739413
2.7.3 :335 > 4.0.to_i
 => 4
2.7.3 :336 > 4 == 4.0
 => true
2.7.3 :337 > 3.7416573867739413.to_i
 => 3

来一个跟今天主题比较有关的解法

def is_perfect_square(num)
  (0..num).bsearch { |int| int*int >=num } ** 2 == num
end

#加工一下 比较看得懂
int = (0..num).bsearch { |x| x*x >=num }
int ** 2 == num

bsearch{}https://ruby-doc.org/core-3.0.2/Range.html#method-i-bsearch

By using binary search, finds a value in range which meets the given condition in O(log n) where n is the size of the range.

2.7.3 :340 > [3, 1, 6, 7].bsearch {|num| num > 5 }
 => 6
2.7.3 :341 > [7, 1, 6, 3].bsearch {|num| num > 5 }
 => 6
#单纯条件下,会找到最接近的答案传出

2.7.3 :345 > (0..100).bsearch { |x| x*x >= 240 }
 => 16
2.7.3 :346 > 16 * 16
 => 256

嗯..杀鸡不必用牛刀。
但牛刀可以杀牛以下的生物?
虽然感觉还是像跑回圈一个一个检查,用在这题反而觉得没有**0.5好用,可是需要复杂判断时,就会比较万用噜。


今日提到的
1.Block是什麽?
2.Proc, lambda的差别?
3.leetcode367. Valid Perfect Square


<<:  C# 入门之逻辑判断(上)

>>:  [Day04] CH03:各式运算子(上)

Day10-流量限制(五)

前言 前几天终於把限制请求「数量」的部分讲完了,但光是限制数量是不够的,最好还能限制每个请求的大小,...

Day 01:前言 - 打开地图,开始我们的旅程吧!

自我介绍 大家好,我是你们这30天的向导,我叫Andy Chiang,目前就读中兴大学资工系,大二升...

Day12 - 状态机与现实世界的落差 - 2: State Explosion(状态大爆炸)

回想一下至今认识过的状态机范例里,我们目前所见的,都是针对一个物体、物件的小小部分出发,比如 RPG...

[Day11] Flutter with GetX flutter toast & Overlay

Flutter toast 在等待http response 或者需要等待的情境 通常会用到转圈圈 ...

Day 2

登入 前往官方文件,查得 登入的范例程序码。 https://sinotrade.github.io...