iT邦幫忙

2021 iThome 鐵人賽

DAY 9
0
自我挑戰組

初級紅寶石魔法師心得分享。系列 第 9

D-21. & 、meta programming & Monkey patch

&

常見使用&的狀況如下

:007 > [1, 2, 3].map(&:to_s)
 => ["1", "2", "3"]

如果你的畫面中字體有分顏色,可以發現&:to_s顏色不同。
Ruby中沒有&:這個東西,其實是&symbol的組合。

間單的回答通常就是&生成{|nun|num }

:015 > [1, 2, 3].map {|int| int.to_s}
 => ["1", "2", "3"]
:016 > [1, 2, 3].map(&:to_s)
 => ["1", "2", "3"]

方法變參數的寫法。

:032 > [1, 2, 3].each(&method(:puts))
1
2
3
 => [1, 2, 3]

而比較準確一點的說&object,&object的關係是。
1.如果objectproc&會把proc轉換成block
proc用多了會忘記block != proc

以下有兩種用法

def i_need_a_block(&block)
  puts "所以需要call"
  block.call
end

 :005 > i_need_a_block {puts "Block here!"}
所以需要call
Block here!

還有介紹過的yield

def i_need_another_block
  puts "我先不想看到&"
  yield
end

:010 > new_proc = proc {puts "但是不能沒有&" }
 => #<Proc:0x00007fb0981cde78 (irb):10>
 :011 > i_need_another_block(&new_proc)
我先不想看到&
但是不能沒有&

2.如果&對象不是proc,那就是&會試圖調動對象內的to_proc方法。
這是symbolto_proc描述。

Returns a Proc object which responds to the given method by sym.

(1..3).collect(&:to_s)  #=> ["1", "2", "3"]

如果不想深究,可以記住的確就是協助產生{|num| num }這樣的block
如果有興趣。符號中的to_proc的定義方式大概長得像這樣。

class Symbol
  def to_proc
    ->(obj, args = nil) { obj.send(self, *args) }
  end
end

下面的code出自於https://maximomussini.com/posts/ruby-to_proc/
說明了converts it to a block.

names.map &:to_s

# We can expand it to  &呼叫了symbol的to_proc
names.map &:to_s.to_proc

# Replacing "to_proc" with the result of calling the method
# 將上面的定義帶入
names.map &->(name, args = nil) { name.send(:to_s, *args) }

# "map" passes a single argument to the block, so we can simplify
# map本身就具將參數迭代可看一段
names.map &->(name) { name.send(:to_s) }

# Calling the method directly we get
names.map &->(name) { name.to_s }

# Since "&" transforms Procs and Lambdas to blocks, it's equivalent to
names.map { |name| name.to_s }

另外提到了一個重點。

There’s nothing special about the shorthand &:method syntax. Ruby arbitrarily defines Symbol#to_proc in a way that allows programmers to avoid some boilerplate.
是的。願意的話可以改寫to_proc功能。

還可以看看&的炫酷用法。

:043 > %w(abs msd sddf).map(&:size).reduce(&:+)  #其實reduce裡的&還可以省略
 => 10
:033 > hash = {a: 123, b: 456, c: 789}
 => {:a=>123, :b=>456, :c=>789}
:034 > [:a, :b, :c].map(&hash)
 => [123, 456, 789]

Monkey patchmeta programming

meta programming是一個概念。

meta programming常被翻譯元程式設計,程式設計大概懂,就....
meta曾經看過一篇文章,忘記出處了,解釋原文是希臘文,有之後超越的意思在,後面衍生為很像是about,例如metadata == data about data

直接看兩句詞典的meta例句:

It's a meta joke. It's sort of a joke about jokes.
這本身就是一個笑話。是一個關於笑話的笑話。

I am standing at my desk typing this about the media right now. It feels very meta.
我現在正站在辦公桌前打這篇關於媒體的文章。我感覺就是在寫自己。

很抽象,但看了這兩句就懂維基百科說的了。

維基百科
元程式設計(英語:Metaprogramming),又譯超程式設計,是指某類電腦程式的編寫,這類電腦程式編寫或者操縱其它程式(或者自身)作為它們的資料,或者在執行時完成部分本應在編譯時完成的工作。多數情況下,與手工編寫全部代碼相比,程式設計師可以獲得更高的工作效率,或者給與程式更大的靈活度去處理新的情形而無需重新編譯。

在Ruby這類動態語言且將資料都包裝成一個個類別,我們處理任何object都是用Class本身自己的程式方法去處理,各Class已經將原本就抽象的object特性封裝好了,就是一種meta programming方式,且因為這種方式,Ruby也能將本身語言非常口語化。

:012 > "reverse".reverse
 => "esrever"

Monkey patch是種描述。

open class也是前幾天code中,一直有在做的事情。
Ruby自由到已經內建好的Class也可以讓你修改。

:001 > [1, 2, 3].to_s
 => "[1, 2, 3]"

class Array
  def to_s
    self.map{|num|num.to_s}  #這裡的to_s是Integer的to_s
  end
end

:007 > [1, 2, 3].to_s
 => ["1", "2", "3"]

當然也會衍生到這類open過的class_methods應該集中某處避免debug找不到,或是如何保留原本array.to_s功能等。

不過針對Monkey pathchRuby就是非常簡單的,我們為了增加子類別功能或更改其中一個方法的作用,使用open class這項這性,對子類別的某個與上層同名的功能做修改的行為。
當然有人說,Monkey pathch屬於不好的操作,易造成程式碼雜亂,為了在執行層面得到正確資料,而對方法實施修改等。但學會將程式碼整理好,不為了交差隨便了事,才是成年人。況且,針對功能擴充應在於moduleopen class的用意是在讓親子層之間有所差異而操作。

判斷句縮寫,三元運算子

if aaa
  bbb
end
 => return bbb if aaa #return可省略

if aaa
  bbb
else
  ccc
end
 => aaa ? (bbb) : (ddd)  #()可省略,但是要注意有些方法不()起來會導致語法錯誤。

if aaa
  bbb
elsif ccc
  ddd
else
  eee
end
 => aaa ? bbb :(ccc ? ddd : eee) #()我沒有勇氣省略...

關於Ruby還有很多可以說。
但還是該進到Rails部分了,雖然一些問題是從Ruby衍生而來,但說明Rails的部分,自然Ruby也會說明到了。


今天的:Leetcode
392.Is Subsequence (指針題)
414.Third Maximum Number (單純語法熟練度題)

392.Is Subsequence
題目連結:https://leetcode.com/problems/is-subsequence/
題目重點:會達成true,代表t一定是a的各元素中間穿插新的元素。

def is_subsequence(s, t)

end

puts ("abc", "ahbgdc") #=>true
puts ("axc", "ahbgdc") #=>false

畫圖時間,及我將例子更改為數字陣列。

[1, 6, 3, 7, 5] #=t
 &
 $        #慢指針 一開始設計為0
[1, 3, 5] #=s

#我們由t的指針當快指針
#index = 0

[1, 6, 3, 7, 5] #t第一個是1,請問s[0]是1嗎?
 &
    $
[1, 3, 5] #=s是的,那我+1,請你幫我檢查第2個。 index += 1

[1, 6, 3, 7, 5] #t第二個是6,請問s[1]是6嗎?
    &
    $
[1, 3, 5] #=s 不是,我不動,請繼續檢查。

[1, 6, 3, 7, 5] #t第三個是3,請問s[1]是6嗎?
       &
       $
[1, 3, 5] #=s 是,那我+1,請你幫我檢查第3個。 index += 1

[1, 6, 3, 7, 5] #t第四個是7,請問s[2]是7嗎?
          &
       $
[1, 3, 5] #no, next plz!

[1, 6, 3, 7, 5] #t第五個是5,請問s[2]是5嗎?
             &
       $
[1, 3, 5] #yes, index += 1。

t如果超過五個,要繼續跑也沒關係,不可能再判斷出相等,index也不會再+=

def is_subsequence(s, t)
  index = 0
  t.each_char do |str|
    index += 1 if str == s[index]
  end
  index == s.size
end

414.Third Maximum Number
題目連結:https://leetcode.com/problems/third-maximum-number/
題目重點:是不是還記得Rubymax可以抓指定前幾個大的。少於三個元素的回報最大的就好。
max(?)太少用忘掉時,差點撞牆

def third_max(nums)

end

puts third_max([3,2,1])  #=> 1
puts third_max([1,2])  #=> 2
puts third_max([2,2,3,1])  #=> 2

題目第三個例子有提醒要uniq,還有元素少於3個回最大

 nums.max if nums.size < 3
 nums.uniq.max

第三大的 == 抓前三取最小

 nums.uniq.max(3).min

錯誤

def third_max(nums)
  nums.size > 2 ? nums.uniq.max(3).min : nums.max
end

因為例子有[1,1,2]。需要uniq。

可行的

def third_max(nums)
  nums.uniq.size > 2 ? nums.uniq.max(3).min : nums.max
end

今天提到的
1.& 是什麼
2.meta programming
3.Monkey pathch
4.Is Subsequence && Third Maximum Number


上一篇
D-22. 繼承(繼承鏈問題)、模組(extend、include、prepend差異) && Add to Array-Form of Integer
下一篇
D-20. 預設更改DBMS 、bundle指令 、Gemfile && Reverse String II && III
系列文
初級紅寶石魔法師心得分享。30

尚未有邦友留言

立即登入留言