Arrays 如何更改Ruby中内置数组类的功能?
我正在用Ruby编写代码。 假设我有这样一个数组:Arrays 如何更改Ruby中内置数组类的功能?,arrays,ruby,Arrays,Ruby,我正在用Ruby编写代码。 假设我有这样一个数组: myarray = [1,2,3,4,5] 我想更改Array类的代码,这样myarray[7]将返回“out-of-bounds”,而不是nil 我不想这样做,我检查是否有东西超出了界限,然后返回“超出界限”,我想直接在数组中实现它 这可能吗?我必须扩展数组类吗 我不知道如何开始,对ruby来说很陌生。我最初的想法是重写Array类中的一个方法,但当我不知道myarray[I]中使用的是什么方法时,我不知道该怎么做。您可能知道Ruby中的类
myarray = [1,2,3,4,5]
我想更改Array类的代码,这样myarray[7]将返回“out-of-bounds”,而不是nil
我不想这样做,我检查是否有东西超出了界限,然后返回“超出界限”,我想直接在数组中实现它
这可能吗?我必须扩展数组类吗
我不知道如何开始,对ruby来说很陌生。我最初的想法是重写Array类中的一个方法,但当我不知道myarray[I]中使用的是什么方法时,我不知道该怎么做。您可能知道Ruby中的类定义如下:
class-Foo
德福
“福”
结束
结束
Foo.new.Foo#在'Foo'的实例上调用实例方法'Foo'`
#=>“foo”
Ruby中的类和模块可以“重新打开”以进行重新定义(尽管这有点不受欢迎——这使得跟踪代码变得困难b/c代码可以在任何一个/任何地方):
class-Foo
def棒
“酒吧”
结束
德福
“覆盖foo”
结束
结束
f=Foo.new
f、 酒吧
#=>“巴”
f、 福
#=>“覆盖foo”
这意味着您还可以执行以下操作:
类数组
#重新定义你想要的任何东西
结束
#或者:
Array.class_eval do
#在大多数方面与上述相同
结束
尽管更不推荐修改核心Ruby类的现有方法。它不仅打破了其他Ruby开发人员对代码的假设(即误导性代码),而且它的全局效应可能会破坏其他库中的行为,等等。TL;博士
这是一个关于如何monkeypatch核心Ruby类(如Array)的有趣问题。但是,如果我没有指出它涉及一些看似不必要的hijinks,那我就是失职了,因为你可以用一个简单的逻辑OR运算符得到同样的行为,比如:
[1, 2, 3][5] || 'out of bounds'
#=> "out of bounds"
尽管如此,如果您想要更改核心类的行为,那么它肯定可以做到
重新打开核心类
在Ruby中,几乎所有不是关键字的东西都是方法。实际上,它只是一种索引到数组对象的方法,解释器提供了一点语法糖,允许将其参数放在括号内。因为它只是由Array类定义的一个实例方法,所以可以通过多种方式对其进行修改,例如重新打开核心类以修改该方法,或者通过在实例上创建一个单例方法。第一种方法在概念上更简单,但对于现实编程来说,单例方法可能是最安全的解决方案
您可以重新打开数组类并重新定义数组::[]
,如下所示:
class Array
# save off a copy of the existing method; we still need it
alias_method :'old_[]', :'[]' unless defined? Array::old_[]
# pass all arguments to the old method, but return a string
# when the result is falsey
def [] *args
send("old_[]", *args) || 'out of bounds'
end
end
然后,您可以演示它的工作原理与常规数组类似,但符合您的预期行为:
a = [10, 20, 30]
a[0] #=> 10
a[0,2] #=> [10, 20]
a[100] #=> "out of bounds"
添加一个单例方法
虽然上面的代码可以工作,但通常monkeypatch一个核心类(如数组)是个坏主意。如果您不想将此行为封装在子类中(例如,classmyarray
),则应使用(使用)或类似以下的单例方法来减少更改的范围。例如:
a = [10, 20, 30]
class << a
alias_method :'old_[]', :'[]' unless self.respond_to? 'old_[]'
def [] *args
send("old_[]", *args) || 'out of bounds'
end
end
a=[10,20,30]
类假设
arr = [1,2,3,4,5]
让我们从检查文档开始。如图所示,该方法可以有一个整数参数:
arr[2] #=> 3
arr[-2] #=> 4
arr[5] #=> nil
arr[-7] #=> nil
arr['x'] #=> TypeError (no implicit conversion of String into Integer)
arr[1..3] #=> [2, 3, 4]
arr[1...3] #=> [2, 3]
arr[-5..-2] #=> [1, 2, 3, 4]
arr[3..9] #=> [4, 5]
arr[7..10] #=> nil
arr[1..'x'] #=> ArgumentError (bad value for range)
或者它可能有两个整数参数:
arr[1,3] #=> [2, 3, 4]
arr[-3,2] #=> [3, 4]
arr[3,5] #=> [4, 5]
arr[7,4] #=> nil
arr[2,'x'] #=> TypeError (no implicit conversion of String into Integer)
或者它可能有一系列整数作为参数:
arr[2] #=> 3
arr[-2] #=> 4
arr[5] #=> nil
arr[-7] #=> nil
arr['x'] #=> TypeError (no implicit conversion of String into Integer)
arr[1..3] #=> [2, 3, 4]
arr[1...3] #=> [2, 3]
arr[-5..-2] #=> [1, 2, 3, 4]
arr[3..9] #=> [4, 5]
arr[7..10] #=> nil
arr[1..'x'] #=> ArgumentError (bad value for range)
据我所知,您希望更改数组#[]
,使其行为保持不变,除非其参数是单个整数,表示数组中超出边界的偏移量。1。这可以通过在一个模块中编写方法,然后使用该方法将该模块前置到类Array
我们看到了
Array.ancestors
#=> [NewSlice, Array, Enumerable, Object, Kernel, BasicObject]
显示NewSlice#[]
中的super
在NewSlice
前面加上Array
后调用原始方法Array[/code>。由于super
没有参数,因此传递给新的Array#[]
方法(在NewSlice
中定义)的所有参数都会传递给原始的Array#[]
在Ruby v2.0中引入Module#prepend
之前,别名在Ruby中很常用。它们仍然在使用,但自从prepend
可用以来,它们的重要性大大降低
如果nil
在Array#[]
返回nil
的所有情况下都要替换为“越界”
,请写入
module NewSlice
def [](*args)
super || "out of bounds"
end
end
一,。是否建议修改核心方法,如Array#[]
是一个单独的问题,但这可能会导致痛苦和痛苦。如果您真的想破坏现有代码(标准类和破坏当前数组类本身的方法,它们依赖于当前行为),当然,您可以monkeypatch它-打开它并以您喜欢的任何方式重新定义该方法,正如在对该问题的各种回答中所概述的那样
一种更明智的方法是将数组子类化,即
class BoundCheckingArray < Array
# redefined the methods according to your taste.
end
类边界检查数组
这当然违反了法律
另一种方法是定义一个完整的独立类,通过将未显式重写的方法委托给底层数组,避免从头重写所有内容
在这两种情况下,您都需要注意,如果BoundCheckingArray中的那些方法假定可以安全地访问边界之外的数组,那么它们将停止工作,因为您没有显式重写它们,因此它们是从BoundCheckingArray中获取的
另一种可能是不修改现有的数组方法,而只是添加绑定检查setter和getter,即使用monkeypatching:
class Array
def at_checked(i)
fail "out_of_bounds" if i >= size || i < -size
at(i)
end
def set_checked(i, new_value)
# analoguous
end
end
类数组
def at_已检查(i)
如果i>=size | | i<-size,则“越界”失败
at(i)
结束
def set_已检查(i,新_值)
#相似的
结束
结束
module NewSlice
def [](*args)
super || "out of bounds"
end
end
Array.prepend(NewSlice)
arr[5] #=> "out of range"
arr[-7] #=> "out of range"
arr[7,4] #=> "out of range"
arr[7..10] #=> "out of range"
class BoundCheckingArray < Array
# redefined the methods according to your taste.
end
class Array
def at_checked(i)
fail "out_of_bounds" if i >= size || i < -size
at(i)
end
def set_checked(i, new_value)
# analoguous
end
end