2016-08-22 11 views
3

以下のルビコードを実行すると、配列がグローバルに操作されるのはなぜですか?そして、関数のスコープ内でのみ配列を操作できるようにするにはどうすればよいですか?Rubyで配列をローカル関数(関数内)でのみ操作できるようにするにはどうすればよいですか?

a = [[1,0],[1,1]] 

def hasRowsWithOnlyOnes(array) 
    array.map { |row| 
    return true if row.keep_if{|i| i != 1 } == [] 
    } 
    false; 
end 

puts a.to_s 
puts hasRowsWithOnlyOnes(a) 
puts a.to_s 

$ ruby test.rb出力:

[[1, 0], [1, 1]] 
true 
[[0], []] 

私はそれを動作させることはできません。私も.select{true}を試して、それを新しい名前に割り当てました。 Ruby for Arraysのスコープはどのように機能しますか? は参考のために、$ ruby -v

ruby 2.2.1p85 (2015-02-26 revision 49769) [x86_64-linux] 
+2

Rubyの新機能で、答えがあなたに何かを意味するかどうか分かりませんが、http://stackoverflow.com/questions/1872110/is-ruby-pass-byをご覧ください。あなたのコードが何をしているのかを理解するのに役立つかどうかを教えてください。 –

+1

'keep_if'を' select'に変更して、必要に応じてコードを動作させますが、以下の@ WayneConradの勧告を使う方がはるかに優れています。 –

答えて

7

それはスコープの問題ではない、それはRubyで

  • すべての変数は、オブジェクトへの参照である引数渡し問題です。

  • オブジェクトをメソッドに渡すと、そのオブジェクトの参照のコピーが作成され、オブジェクトに渡されます。あなたの方法でその変数arrayを意味し、トップレベルの変数aが正確に同じ配列を参照してください

arrayに加えられた変更は、両方の変数が同じオブジェクトを参照するため、aへの変更としても表示されます。

あなたのメソッドはArray#keep_ifを呼び出して配列を変更します。 keep_ifメソッドは、配列をインプレースで変更します。

修正

は、このための最良の修正は、あなたの方法を渡された配列を変更しないようにそれを作ることです。これは、Enumerable#any?Enumerable#all?方法を使用して、かなりきちんと行うことができます。

def has_a_row_with_only_ones(array) 
    array.any? do |row| 
    row.all? { |e| e == 1 } 
    end 
end 

このコードは、任意の行に対して、その行のすべての要素が1である場合、このメソッドはtrueを返します。これらのメソッドは配列を変更しません。もっと重要なのは、メソッドの意図を明確に伝達することです。配列は、その変更は、メソッドの外に知られることなく変更することができるように

貧しい回避策

あなたは方法は、配列のコピーを通じてとして機能するようにしたい場合は、それに渡された、あなたが作ることができます配列のディープコピー。 this answerに示すように、あなたは深いコピーを作成するために、このメソッドを定義することができます。

def deep_copy(o) 
    Marshal.load(Marshal.dump(o)) 
end 

次に、この方法の一番上に、深いコピーを作成します:それはだから

def has_a_row_with_only_ones(array) 
    array = deep_copy(array) 
    # code that modifies array 
end 

は、これは避けるべきですスロー。

0

Rubyは値によってパス

経由sitepoint

あるRubyはあなたがおそらく 既に知っている異なるスコープ内で定義された変数を、持っています。ほとんどのチュートリアルでは簡単に説明していますが( 変数タイプ)、そのスコープが正確に何であるかについては言及していません。 ここで

の詳細は以下のとおりです。

クラス変数(@@a_variable):クラス定義と任意のサブクラスから利用可能。外のどこからでも利用することはできません。

インスタンス変数(@a_variable):クラスインスタンス内のすべてのメソッド間で特定のオブジェクト内でのみ使用できます。クラス定義から直接利用することはできません。

グローバル変数($a_variable):Rubyスクリプトのどこでも使用できます。

ローカル変数(a_variable):スコープによって異なります。彼らのスコープはさまざまなものに依存しているため、これらの機能を最大限に活用して問題に遭遇します。

+1

'a'はグローバルではありません。 Rubyのグローバル変数は '$'で始まります(何も使用しないでください)。 –

+0

その通りです。気づいてくれてありがとう! –