Ruby中少數天生不屬於物件的存在。
未物件化前,只能依附在有設計好的方法後面才有作用。
2.7.3 :064 > 10.to_s {|string| string.to_i}
=> "10"
#後方想再to_i也沒用。
程式碼區塊指的是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。
哪個好看用哪個...
不是的,是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>
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 "我跑出去了" }
我跑出去了
我跳進來了
我跑出去了
我又跳進來了
還有一個就是物件化。
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。
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?
寫法
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