2

当我执行下面的代码时,我的数组“任务”最终会得到与数据库中每一行重复的 dbi 调用中相同的最后一行。

require 'dbi'
require 'PP'

dbh = DBI.connect('DBI:SQLite3:test', 'test', '')

dbh.do("DROP TABLE IF EXISTS TASK;")
dbh.do("CREATE TABLE TASK(ID INT, NAME VARCHAR(20))")   

# Insert two rows
1.upto(2) do |i|
    sql = "INSERT INTO TASK (ID, NAME) VALUES (?, ?)"
    dbh.do(sql, i, "Task #{i}")
end

sth = dbh.prepare('select * from TASK')
sth.execute

tasks = Array.new

while row=sth.fetch do
    p row
    p row.object_id
    tasks.push(row)
end

pp(tasks)

sth.finish

因此,如果我的 TASK 表中有两行,那么不要在任务数组中获取它:

[[1, "Task 1"], [2, "Task 2"]]

我明白了

[[2, "Task 2"], [2, "Task 2"]]

完整的输出如下所示:

[1, "Task 1"]
19877028
[2, "Task 2"]
19876728
[[2, "Task 2"], [2, "Task 2"]]

我究竟做错了什么?

4

4 回答 4

2

行对象中似乎有一些奇怪的行为,似乎是某种单例,这就是 dup 方法无法解决它的原因。

跳转到源代码似乎 to_a 方法将复制内部行元素,这就是它起作用的原因,因此答案是在行对象上使用 to_a ,或者如果您愿意,也可以将其转换为 Hash 以保留元数据。

while row=sth.fetch do
  tasks.push(row.to_a)
end

但我推荐更红宝石的方式

sth.fetch do |row|
  tasks << row.to_a
end
于 2012-11-18T23:02:56.470 回答
1

你确定你已经完全复制了你的代码吗?AFAIK 您编写的代码根本不应该工作......您混合了两种不打算以这种方式使用的结构。

假设您来自 C 或 Java 背景,我错了吗?ruby 中的迭代非常不同,让我尝试解释一下。

ruby 中的 while 循环具有以下结构:

while condition
  # code to be executed as long as condition is true
end

带有块的方法具有以下结构:

sth.fetch do |element|
  # code to be executed once per element in the sth collection
end

现在有一些非常重要的东西要理解 : fetch,或者任何其他这种在 ruby​​ 中的方法,都不是你在 C 中遇到的迭代器——你不必再次调用它,直到迭代器到达末尾收藏。

您只需调用一次,并给它一个作为参数,这是一种匿名函数(如在 javascript 中)。然后,该fetch方法将一个接一个地将集合的每个元素传递(“yield”)到该块。

所以在你的情况下正确的语法应该是:

sth.fetch do |row|
  p row
  tasks.push row
end

可以用更“老派”的方式写成这样:

# define a function
# = this is your block
def process( row )
  p row
  tasks.push row
end

# pass each element of a collection to this function
# = this is done inside the fetch method
for row in sth
  process row
end

我建议你阅读更多关于 blocks / procs / lambdas 的内容,因为它们在 ruby​​ 中无处不在,恕我直言,这是这种语言如此棒的原因之一。迭代器只是一个开始,你可以用这些做更多的事情......如果你需要好的参考文档,被认为是 ruby​​ists 中最好的来源之一,如果你愿意,我可以告诉你更多。

于 2012-11-18T22:54:18.003 回答
0

我不知道您的代码是如何完全工作的,但我想如果您更改tasks.push(row)tasks.push(row.dup),那么它应该可以工作。如果是这种情况,那么sth.fetch每次都会给你相同的数组(相同的对象 id),即使它的内容被更新,并且你tasks重复地推入同一个数组。

于 2012-11-18T05:12:13.043 回答
0

有很多事情可以发生,但试试这个。

首先确保使用parens将块传递给while。

while (row=sth.fetch) do
    p row
    tasks.push(row)
end

然后是惯用的红宝石方式

sth.fetch do |row|
    p row
    tasks << row # same as push
end
于 2012-11-18T05:32:39.937 回答