Menggabungkan array yang diurutkan dengan Python
def merge_arrays(list1, list2):
len_list1 = len(list1); len_list2 = len(list2)
merge_len = len_list1 + len_list2
merge_list = []
l1_ptr = 0
l2_ptr = 0
# import pdb; pdb.set_trace()
while(l1_ptr <= len_list1-1 and l2_ptr <= len_list2-1):
if (list1[l1_ptr] <= list2[l2_ptr]):
merge_list.append(list1[l1_ptr])
l1_ptr += 1
elif (list1[l1_ptr] > list2[l2_ptr]):
merge_list.append(list2[l2_ptr])
l2_ptr += 1
if l1_ptr > len_list1-1: #list1 exhausted
for item in list2[l2_ptr:]:
merge_list.append(item)
else:
for item in list1[l1_ptr:]:
merge_list.append(item)
return merge_list
Saya mencoba menggabungkan array yang diurutkan dengan python. Bagaimana saya bisa meningkatkan ini? Sejujurnya sepertinya saya telah menulis ini dalam C dan bukan dengan Python
Jawaban
berbagai
merge_lentidak terpakai- tanda kurung tambahan di sekitar pemeriksaan sederhana tidak diperlukan
l1_ptr <= len_list1-1dapat dibuat lebih jelas sebagail1_ptr < len_list1- menggunakan nama variabel
l1_ptruntuk menyimpan beberapa karakter sambil membuatnya lebih sulit untuk menebak dari nama apa fungsinya tidak berguna
Bekerja dengan indeks secara langsung memang tidak benar-benar pythonic. Anda dapat membuatnya lebih umum, menggunakan iterand next, dan berfungsi untuk semua iterable.
mengetik
tambahkan informasi pengetikan:
import typing
T = typing.TypeVar("T")
def merge_sorted_iterables(
iterable1: typing.Iterable[T], iterable2: typing.Iterable[T]
) -> typing.Iterable[T]:
Ini adalah penjelasan tambahan untuk pengguna fungsi ini (dan IDE-nya).
doktrin
Tambahkan beberapa penjelasan tentang apa yang dilakukan metode, harapkan dari pemanggil, dan kembali.
def merge_sorted_iterables(
iterable1: typing.Iterable[T], iterable2: typing.Iterable[T]
) -> typing.Iterable[T]:
"""Merge 2 sorted iterables.
The items in the iterables need to be comparable (and support `<=`).
...
"""
pembuat ulang
Alih-alih melacak indeks, Anda dapat menggunakan iterdan next. Anda bahkan tidak perlu menambahkan item ke daftar, Anda dapat yieldmelakukannya, sehingga pemanggil metode dapat memutuskan dengan cara apa dia ingin menggunakan ini.
done = object()
iterator1 = iter(iterable1)
iterator2 = iter(iterable2)
item1 = next(iterator1, done)
item2 = next(iterator2, done)
while item1 is not done and item2 is not done:
if item1 <= item2:
yield item1
item1 = next(iterator1, done)
else:
yield item2
item2 = next(iterator2, done)
Maka yang perlu dilakukan hanyalah melanjutkan iterator yang belum selesai
if item1 is not done:
yield item1
yield from iterator1
if item2 is not done:
yield item2
yield from iterator2
import typing
T = typing.TypeVar("T")
def merge_sorted_iterables(
iterable1: typing.Iterable[T], iterable2: typing.Iterable[T]
) -> typing.Iterable[T]:
"""Merge 2 sorted iterables.
The items in the iterables need to be comparable (and support `<=`).
...
"""
done = object()
iterator1 = iter(iterable1)
iterator2 = iter(iterable2)
item1 = next(iterator1, done)
item2 = next(iterator2, done)
while item1 is not done and item2 is not done:
if item1 <= item2:
yield item1
item1 = next(iterator1, done)
else:
yield item2
item2 = next(iterator2, done)
if item1 is not done:
yield item1
yield from iterator1
if item2 is not done:
yield item2
yield from iterator2
pengujian
Anda dapat menguji perilaku, dimulai dengan kasus paling sederhana:
import pytest
def test_empty():
expected = []
result = list(merge_sorted_iterables([], []))
assert result == expected
def test_single():
expected = [0, 1, 2]
result = list(merge_sorted_iterables([], range(3)))
assert expected == result
result = list(merge_sorted_iterables(range(3), [],))
assert expected == result
def test_simple():
expected = [0, 1, 2, 3, 4, 5]
result = list(merge_sorted_iterables([0, 1, 2], [3, 4, 5]))
assert result == expected
result = list(merge_sorted_iterables([0, 2, 4], [1, 3, 5]))
assert result == expected
result = list(merge_sorted_iterables([3, 4, 5], [0, 1, 2],))
assert result == expected
def test_string():
expected = list("abcdef")
result = list(merge_sorted_iterables("abc", "def"))
assert result == expected
result = list(merge_sorted_iterables("ace", "bdf"))
assert result == expected
result = list(merge_sorted_iterables("def", "abc",))
assert result == expected
def test_iterable():
expected = [0, 1, 2, 3, 4, 5]
result = list(merge_sorted_iterables(iter([0, 1, 2]), iter([3, 4, 5])))
assert result == expected
result = list(merge_sorted_iterables(iter([0, 2, 4]), iter([1, 3, 5])))
assert result == expected
result = list(merge_sorted_iterables(iter([3, 4, 5]), iter([0, 1, 2]),))
assert result == expected
def test_comparable():
with pytest.raises(TypeError, match="not supported between instances of"):
list(merge_sorted_iterables([0, 1, 2], ["a", "b", "c"]))
menurun
Setelah Anda memiliki tes ini, Anda dapat dengan mudah memperluas perilaku untuk juga mengambil iterables turun:
import operator
def merge_sorted_iterables(
iterable1: typing.Iterable[T],
iterable2: typing.Iterable[T],
*,
ascending: bool = True,
) -> typing.Iterable[T]:
"""Merge 2 sorted iterables.
The items in the iterables need to be comparable.
...
"""
done = object()
iterator1 = iter(iterable1)
iterator2 = iter(iterable2)
item1 = next(iterator1, done)
item2 = next(iterator2, done)
comparison = operator.le if ascending else operator.ge
while item1 is not done and item2 is not done:
if comparison(item1, item2):
yield item1
item1 = next(iterator1, done)
else:
yield item2
item2 = next(iterator2, done)
if item1 is not done:
yield item1
yield from iterator1
if item2 is not done:
yield item2
yield from iterator2
Saya menambahkan ascendingkata kunci sebagai argumen kata kunci saja untuk menghindari kebingungan dan menjaga kompatibilitas ke belakang
Salah satu tesnya:
def test_descending():
expected = [5, 4, 3, 2, 1, 0]
result = list(
merge_sorted_iterables([2, 1, 0], [5, 4, 3], ascending=False)
)
assert result == expected
result = list(
merge_sorted_iterables([4, 2, 0], [5, 3, 1], ascending=False)
)
assert result == expected
result = list(
merge_sorted_iterables([5, 4, 3], [2, 1, 0], ascending=False)
)
assert result == expected
Gunakan indentasi 4 spasi .
Jangan berulang kali mengurangi 1 dari nilai yang sama yang tidak berubah.
Sederhanakan kondisi perbandingan: cukup gunakan else.
Manfaatkan list.extend() .
Lepaskan persyaratan penutup: sebenarnya tidak diperlukan. Kode seperti zs.extend(xs[xi:])akan berfungsi dengan baik bahkan jika ximelebihi batas daftar.
Persingkat nama variabel untuk meringankan bobot kode dan meningkatkan keterbacaan. Tidak ada kehilangan makna di sini, karena semua nama pendek cukup konvensional dan masuk akal dalam fungsi generik seperti ini.
def merge_arrays(xs, ys):
# Setup.
xmax = len(xs) - 1
ymax = len(ys) - 1
xi = 0
yi = 0
zs = []
# Compare and merge.
while xi <= xmax and yi <= ymax:
if xs[xi] <= ys[yi]:
zs.append(xs[xi])
xi += 1
else:
zs.append(ys[yi])
yi += 1
# Merge any remainders and return.
zs.extend(ys[yi:])
zs.extend(xs[xi:])
return zs
Tadi malam saya menulis solusi berbasis iterator tetapi entah bagaimana lupa yang next()mendukung defaultargumen praktis: kodenya canggung dan Maarten Fabré melakukan implementasi yang lebih baik. Tetapi jika Anda ingin menggunakan more_itertools.peekable() Anda dapat mencapai implementasi yang sederhana dan mudah dibaca. Terima kasih kepada hujan luar biasa untuk ide di komentar yang membantu saya menyederhanakan lebih lanjut.
from more_itertools import peekable
def merge(xs, ys):
xit = peekable(xs)
yit = peekable(ys)
while xit and yit:
it = xit if xit.peek() <= yit.peek() else yit
yield next(it)
yield from (xit or yit)