[英]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_arg
, some_arg
或something_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.