web-dev-qa-db-ja.com

Python Simple Swap Function

Pythonを学習しようとしたときに、この問題に遭遇しました。次の機能を検討してください。

def swap0(s1, s2):
    assert type(s1) == list and type(s2) == list
    tmp = s1[:]
    s1 = s2[:]
    s2 = tmp
return

s1 = [1]
s2 = [2]
swap0(s1, s2)
print s1, s2

S1とs2は何を印刷しますか?

問題を実行した後、printステートメントが1 2を出力することがわかりました。s1とs2の値はswap0関数から変更されていないようです。私が考えることができた唯一の説明は、線のためでした。

tmp = s1[:]

S1 [:]はコピーであるため、これはs1の値が関数呼び出しで変更されないことを意味します。ただし、swap0のパラメーターは(s1、s2)なので、tmp = s1 [:]を実行した後かどうかはわかりません。いつでも

s1 = something...

s1自体ではなく、s1のコピーへの参照になります。誰かがより良い説明を提供できますか?ありがとう。

11
Rhs

これは、s1関数内のs2およびswap0に新しい値を割り当てるためです。これらの割り当ては、関数の外部には伝播しません。関数呼び出しの代わりに関数本体をコピーして貼り付けるだけで機能することがわかります。

引数自体ではなく、引数によって参照されるオブジェクトを変更することにより、この問題を回避できます。

def swap0(s1, s2):
    assert type(s1) == list and type(s2) == list
    tmp = s1[:]
    s1[:] = s2
    s2[:] = tmp

ただし、Pythonでスワップを行うより簡単でより良い方法は単純です:

s1, s2 = s2, s1

これも、リストへの特定の参照のみを交換し、リストの内容自体は交換しません。

23
Thomas

そのまま、最終的なprints1およびs2の元の値を出力します。これは、関数のスコープ内でのみスワップしているためです。そうしても、関数の外部の値には影響しません(つまり、関数が呼び出された後の値の後)

それらが可変タイプ(listsetdictなど)である場合、swap内でインプレースで変更できます。ただし、それはswapが可変型でのみ機能するように制限します。

したがって、入力を逆の順序で返すほうがよいでしょう。

def swap(s1, s2):
    return s2, s1

s1 = 'a'
s2 = 'b'
s1, s2 = swap(s1, s2)
print s1, s2 # prints 'b a'

もちろん、次のようにすべてを1行で実行できます。

s1, s2 = s2, s1

乾杯!

11
inspectorG4dget

他の答えは何が悪いのかを説明しています。これはあなたが望むことをするバージョンです:

def swap(s1, s2):
    assert isinstance(s1, list) and isinstance(s2, list)
    s1[:], s2[:] = s2[:], s1[:]

参照: isinstance vs. type

6
Zero Piraeus

関数内で、local variables s1およびs2右側に値があります(コピーを作成するためにスライスを使用しているため、これもローカルです)。これらのローカル変数の内容を変更しても、同じリストを参照しなくなるため、呼び出しスコープのリストの内容は変更されません。

4
mgilson

目標を達成する1行の関数を次に示します。

swap = lambda x: (x[1], x[0])
2
parallelogram

また、両方のリストの長さが同じ場合、インデックスとループを使用する古いスワッピング方法でこれを行うこともできます。これは一種の古い学校ですが、インデックス作成の理解に役立ちます

a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
b = [0, 9, 8, 7, 6, 5, 4, 3, 2, 1]

for i in range(0, len(a)):
    a[i] = a[i] + b[i]
    b[i] = a[i] - b[i]
    a[i] = a[i] - b[i]

print(a)
print(b)

これにより、出力が次のようになります。

 [0,9,8,7,6,5,4,3,2,1]
 [1,2,3,4,5,6,7,8,9,0]

または、Xorを使用して行うこともできます。 Xor演算子は、たとえばオペランド間でXor演算を行うビット単位の演算子です。

a = 5 #0b101
b = 4 #0b100
c = a ^ b #0b001

ここで、0b101は5のバイナリ表現であり、0b100は4のバイナリ表現であり、これらをXorすると、出力は0b001すなわち1になります。 Xorは、入力が1つで1つだけの場合、1つの出力結果を返します。両方の入力が0であるか、両方が1である場合、出力結果は0です。たとえば、Xorを使用して2つの変数を交換できます。

a = 5        # 0b0101
b = 9        # 0b1001
a = a ^ b    # Xor (0b0101, 0b1001) = 0b1100 (12)
b = a ^ b    # Xor (0b1100, 0b1001) = 0b0101 (5)
a = a ^ b    # Xor (0b1100, 0b0101) = 0b1001 (9)
print("a = {} and b = {}".format(a, b))

出力はa = 9 and b = 5になります

同様に、次の項目に対してXor操作を実行することにより、2つのリストを交換することもできます。

a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 ]
b = [ 0, 9, 8, 7, 6, 5, 4, 3, 2, 1 ] 

for i in range(0, len(a)) :
     a[i] = a[i] ^ b[i] 
     b[i] = a[i] ^ b[i] 
     a[i] = a[i] ^ b[i] 

print(a)
print(b)

出力:

[0, 9, 8, 7, 6, 5, 4, 3, 2, 1]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]

別のシナリオを考えてみましょう。たとえば、リスト内のアイテムを交換する必要がある場合は、このx = [ 13, 3, 7, 5, 11, 1 ]のようなリストがあり、このアイテムをx = [ 1, 3, 5, 7 , 11, 13 ]のように交換する必要があります。 2つのビット演算子Xor ^およびCompliments ~を使用する

コード:

# List of items 
a = [ 13, 3, 7, 5, 11, 1 ]

# Calculated the length of list using len() and
# then calulated the middle index of that list a 

half = len(a) // 2

# Loop from 0 to middle index
for i in range(0, half) :

# This is to prevent index 1 and index 4 values to get swap 
# because they are in their right place.
if (i+1) % 2 is not 0 :

    #Here ~i means the compliment of i and ^ is Xor,
    # if i = 0 then ~i will be -1 
    # As we know -ve values index the list from right to left 
    # so a [-1] = 1 

    a[i] = a[i] ^ a[~i] 
    a[~i] = a[i] ^ a[~i] 
    a[i] = a[i] ^ a[~i]

print(a)

したがって、出力は[1, 3, 5, 7, 11, 13]になります

2
Devil-oper

あなたはこれを持つことができます:

def swap(x , y):
  x , y = y , x
  return x , y

x  = 5
y = 10

print ('x is {0} and y is {1}'.format(x,y))    # x is 5 and y is 10
x , y = swap(x,y)                              # doing swap 
print ('x is {0} and y is {1}'.format(x,y))    # x is 10 and y is 5
1
a_m_dev