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テーブルに2つの行がある場合、これをタスク配列に取得する代わりに、次のようにします。

[[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を使用するか、必要に応じてメタを保持するためにハッシュに変換することができます。

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

コードをそのままコピーしました?あなたが書いたコードはまったく機能しないはずです...あなたはそのように使用されることを意図されていない2つの構造を混ぜ合わせます。

あなたがCまたはJavaのバックグラウンドから来ていると仮定するのは間違っていますか?ルビーでの反復は非常に異なります。説明してみましょう。

ルビーの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、またはルビーのこの種の他のメソッドは、たとえば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

ブロック/procs/ラムダについてもっと読むことをお勧めします。なぜなら、それらはルビーのいたるところにあり、IMHOがこの言語がとても素晴らしい理由の1つだからです。イテレータはまだ始まったばかりです。これらを使用してさらに多くのことを行うことができます...優れたリファレンスドキュメントが必要な場合、つるはしはルビイストの間で最高の情報源の1つと見なされます。必要に応じて、さらに詳しく説明できます。

于 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 に答える