簡體   English   中英

在Ruby中使用String#to_proc實現to_s(2)

[英]Implement to_s(2) with String#to_proc in Ruby

我正在學習一元算子, &

關於在方法調用的參數中使用&有一些很好的問題。 通常格式類似於some_obj.some_method(&:symbol)

似乎主要思想是ruby調用to_proc方法:symbol當一元運算符放在符號前面時符號。 因為Symbol#to_proc存在“一切正常”。

我仍然對一切如何運作感到困惑。

如果我想用字符串實現“to_proc類功能”,該怎么辦? 我把它放在引號中因為我不確定如何談論我正在嘗試做什么。

但目標是編寫一個String#to_proc方法,以便以下工作:

class String
  def to_proc # some args?
    Proc.new do
      # some code?
    end
  end
end
p result = [2, 4, 6, 8].map(&'to_s 2')
#=> ["10", "100", "110", "1000"]

我就是這樣做的:

class String
  def to_proc
    Proc.new do |some_arg|
      parts = self.split(/ /)
      some_proc = parts.first.to_sym.to_proc
      another_arg = parts.last.to_i
      some_proc.call(some_arg, another_arg)
    end
  end
end
p result = [2, 4, 6, 8].map(&'to_s 2')
#=> ["10", "100", "110", "1000"]

我很困惑的主要部分是如何將參數輸入String#to_proc方法。 這好像是:

def to_proc
  Proc.new do |some_arg| ...
end

應該:

def to_proc some_arg
  Proc.new do |yet_another_arg| ...
end

或類似的東西。 [2, 4, 6, 8]值如何進入String#to_proc返回的proc?

寫這個

[2, 4, 6, 8].map { |each| each.to_s(2) }

雖然我猜這不是你想要的......

以下是Symbol#to_proc的實現方式。

class Symbol
  def to_proc
    proc { |each| each.send(self) }
  end
end

如果需要,可以按如下方式在Array上定義to_proc

class Array
  def to_proc
    symbol, *args = self
    proc { |each| each.send(symbol, *args) }
  end
end

然后使用

[2, 4, 6, 8].map(&[:to_s, 2])

另一種選擇是使用curry

雖然這不適用於綁定方法,但您必須首先定義to_s lambda函數。

to_s = lambda { |n, each| each.to_s(n) }

[2, 4, 6, 8].map(&to_s.curry[2])

雖然所有這些看起來更像是學術練習。

當你運行some_method(&some_obj) ,Ruby首先調用some_obj.to_proc來獲取一個proc,然后它將proc“轉換”為一個塊並將該塊傳遞給some_method 那么參數如何進入proc取決於some_method如何將參數傳遞給塊。

例如,當您定義String#to_proc ,它返回一個proc{|arg| ...} proc{|arg| ...} (帶有一個參數的proc),並調用[...].map(&'to_s 2') ,Ruby將其解釋為

[...].map(&('to_s 2'.to_proc))

是的

[...].map(&proc{|arg| ... })

最后

[...].map {|arg| ... }

你的方法的問題是,當它總是作為字符串傳遞時,沒有辦法推斷出參數的類型。

順便說一下,要解決你的問題:

[2,4,6,8]值如何進入String#to_proc返回的proc?

它們在這里是some_arg ,它不是你必須定義的變量,而是一個在調用proc時自動傳遞的參數。

這里是String補丁的重寫和一些用法示例:

class String
  def to_proc
    fn, *args = split ' '
    ->(obj) { obj.send(fn.to_sym, *args) }
  end
end

這適用於以下示例:

p result = [[1,2,3]].map(&"join -")
# => ['1-2-3']

但是失敗了(你的例子):

p result = [2, 4, 6, 8].map(&'to_s 2')
# => TypeError

當2應該是整數而不是字符串時,問題是調用to_s('2') 除了可能的一些序列化之外,我想不出任何解決方法(盡管其他一個答案顯示了eval如何工作)。

既然這種方法的局限性是明確的,那么值得將它與Symbol上更常用的補丁相比較,以便將參數傳遞給proc shorthands(這取自can-you-supply-arguments-to-mapmethod-syntax-in -ruby

class Symbol
  def call(*args, &block)
    ->(caller, *rest) { caller.send(self, *rest, *args, &block) }
  end
end

a = [1,3,5,7,9]
a.map(&:+.(2))
# => [3, 5, 7, 9, 11] 

這樣,您可以將任何類型的參數傳遞給proc,而不僅僅是字符串。

一旦定義了它,就可以輕松地換掉String for Symbol:

class String
  def call(*args, &blk)
    to_sym.call(*args, &blk)
  end
end

puts [1,2,3].map(&'+'.(1))

重構代碼

您可以自由選擇proc塊變量的名稱。 所以它可能是yet_another_argsome_argsomething_else 在這種情況下,您傳遞給to_proc的對象實際上是您想要接收 proc調用的對象,因此您可以將其稱為receiver 方法和參數在String中,因此您可以使用String#split from self

class String
  def to_proc
    proc do |receiver|
      method_name, param = self.split
      receiver.method(method_name.to_sym).call(param.to_i)
    end
  end
end

p result = [2, 4, 6, 8].map(&'to_s 2')
# => ["10", "100", "110", "1000"]

請注意,此方法已定制為接受一個方法名稱和一個整數參數。 它在一般情況下不起作用。

另一種可能性

警告

評估邪惡的

你被警告過了

這適用於您想要的確切語法,它也適用於更廣泛的方法和參數:

class String
  def to_proc
    proc { |x| eval "#{x.inspect}.#{self}" }
  end
end

p [2, 4, 6, 8].map(&'to_s 2')
#=> ["10", "100", "110", "1000"]
p ["10", "100", "110", "1000"].map(&'to_i 2')
#=> [2, 4, 6, 8]
p [1, 2, 3, 4].map(&'odd?')
#=> [true, false, true, false]
p %w(a b c).map(&'*3')
#=> ["aaa", "bbb", "ccc"]
p [[1,2,3],[1,2],[1]].map(&'map(&"*2")')
#=> [[2, 4, 6], [2, 4], [2]]

但它也帶來了安全問題。 擁有權利的同時也被賦予了重大的責任!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM