Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/variables/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Arrays 如何更改Ruby中内置数组类的功能?_Arrays_Ruby - Fatal编程技术网

Arrays 如何更改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中的类

我正在用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