好吧,也许这很简单,但是...... 鉴于此:
arr = ("a".."z").to_a
arr
=> ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
..我正在尝试将所有“arr”值更改为“bad”
为什么这行不通?
arr.each { |v| v = "bad" }
arr
=> ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
答案表明“v”是 block 的局部变量(数组值的“副本”),我完全理解(以前从未让我感到困惑)但是然后
.. 如果数组元素是对象,为什么它可以工作?
class Person
def initialize
@age = 0
end
attr_accessor :age
end
kid = Person.new
man = Person.new
arr = [kid, man]
arr.each { |p| p.age = 50 }
arr[0]
=> #<Person:0xf98298 @age=50>
这里的“p”不是仍然是本地 block 吗? 但它确实影响了对象,怎么会呢?
最佳答案
我将扩展@pst 的评论:
why isn't this working ?
arr.each { |v| v = "bad" }
因为 each遍历数组并将每个项目放入您作为本地变量给出的 block 中 v , 作为 v不是对数组 arr 的引用.
new_arr = arr.each { |v| v = "bad" }
each不返回一个数组,因为你会使用 map (参见@benjaminbenben 的回答)。因此分配它不“工作”。
arr.each { |v| arr[arr.index v] = "bad" }
在这里,您将每个项目放在 arr 中进入局部变量 v ,但是您还在 block 中引用了数组本身,因此您可以分配给数组并使用局部变量 v找到对应于 v 内容的索引(但您可能会发现,当项目并非都是唯一的时,这不会像您预期的那样工作)。
arr.each { |p| p.age = 50 }
kid.age #-> 50
在这里,您再次填充了局部变量 p arr 中的每个项目/对象,但是您已经通过一种方法访问了每个项目,因此您可以更改该项目 - 您没有更改数组。它是不同的,因为引用是对局部变量内容的引用,您将其与对数组的引用混淆了。它们是不同的东西。
回应以下评论:
arr[0]
# => #<Person:0xf98298 @age=50>
关键在于谁在什么时候指的是谁。
试试这个:
v = Person.new
# => #<Person:0x000001008de248 @age=0>
w = Person.new
# => #<Person:0x000001008d8050 @age=0>
x = v
# => #<Person:0x000001008de248 @age=0>
v = Person.new
# => #<Person:0x00000100877e80 @age=0>
arr = [v,w,x]
# => [#<Person:0x00000100877e80 @age=0>, #<Person:0x000001008d8050 @age=0>, #<Person:0x000001008de248 @age=0>]
v那里提到了 2 个不同的对象。 v不是一个固定的东西,它是一个名字。一开始是指#<Person:0x000001008de248 @age=0> ,那么它指的是#<Person:0x00000100877e80 @age=0> .
现在试试这个:
arr.each { |v| v = "bad" }
# => [#<Person:0x00000100877e80 @age=0>, #<Person:0x000001008d8050 @age=0>, #<Person:0x000001008de248 @age=0>]
它们都是对象,但没有任何更新或“工作”。为什么?因为当第一次进入区 block 时,v指的是产生(给定)的数组中的项目。所以在第一次迭代时 v是#<Person:0x00000100877e80 @age=0> .
但是,我们随后分配 "bad"至 v .我们没有分配 "bad"到数组的第一个索引,因为我们根本没有引用数组。 arr是对数组的引用。放arr在 block 内,您可以更改它:
arr.each { |v|
arr[0] = "bad" # yes, a bad idea!
}
为什么 arr.each { |p| p.age = 50 }更新数组中的项目?因为p指的是也恰好在数组中的对象。第一次迭代 p引用对象也称为 kid , 和 kid有一个 age=方法和你坚持50在里面。 kid也是数组中的第一项,但你说的是 kid不是数组。你可以这样做:
arr.each { |p| p = "bad"; p.age }
NoMethodError: undefined method `age' for "bad":String
起初,p引用了也恰好在数组中的对象(这是它产生的地方),然后 p被用来引用"bad" .
each迭代数组并在每次迭代时产生一个值。您只能得到数组的值而不是。如果你想更新一个数组,你可以这样做:
new_arr = arr.map{|v| v = "bad" }
new_arr = arr.map{|v| "bad" } # same thing
或
arr.map!{|v| v = "bad"}
arr.map!{|v| "bad"} # same thing
作为map返回一个数组填充了 block 的返回值。 map!将使用一个数组更新您调用它的引用,填充了 block 的返回值。通常,无论如何在遍历对象时更新对象是个坏主意。我发现将其视为创建一个新数组总是更好,然后您可以使用 !方法作为快捷方式。
关于ruby - 枚举器 `Array#each` 's {block} can' t 总是更改数组值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13022273/
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我有一个字符串input="maybe(thisis|thatwas)some((nice|ugly)(day|night)|(strange(weather|time)))"Ruby中解析该字符串的最佳方法是什么?我的意思是脚本应该能够像这样构建句子:maybethisissomeuglynightmaybethatwassomenicenightmaybethiswassomestrangetime等等,你明白了......我应该一个字符一个字符地读取字符串并构建一个带有堆栈的状态机来存储括号值以供以后计算,还是有更好的方法?也许为此目的准备了一个开箱即用的库?
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
这似乎应该有一个直截了当的答案,但在Google上花了很多时间,所以我找不到它。这可能是缺少正确关键字的情况。在我的RoR应用程序中,我有几个模型共享一种特定类型的字符串属性,该属性具有特殊验证和其他功能。我能想到的最接近的类似示例是表示URL的字符串。这会导致模型中出现大量重复(甚至单元测试中会出现更多重复),但我不确定如何让它更DRY。我能想到几个可能的方向...按照“validates_url_format_of”插件,但这只会让验证干给这个特殊的字符串它自己的模型,但这看起来很像重溶液为这个特殊的字符串创建一个ruby类,但是我如何得到ActiveRecord关联这个类模型
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时
出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits