Re: [問題] 如何比較list裡面各項數字的大小並重組?

看板Python作者 (←這人是超級笨蛋)時間12年前 (2013/08/01 03:37), 編輯推噓7(702)
留言9則, 7人參與, 最新討論串3/5 (看更多)

08/01 01:35,
u = [min(s[i], t[i]) for i in range(len(s))]
08/01 01:35

08/01 01:36,
我發現我的第一感好像跟上面的各位很不一樣......
08/01 01:36

08/01 01:36,
有大大可以點評一下大家的第一感的優劣之處嗎?
08/01 01:36

08/01 01:37,
個人覺得mikapauli的"list(map(min, s, t))"是目前最優
08/01 01:37

08/01 01:39,
除了簡潔(不熟map就不直覺XD)外,我猜執行效率是最優的
08/01 01:39

08/01 01:42,
看了下mikapauli大在原PO上一篇的推文自以為效率也很高
08/01 01:42
直覺這種東西本來就很主觀, 重點是不能為了直覺犧牲太多效率 需要會最少東西的寫法應該是 result = [] for i in range(0, len(s)): # 陣列很長時應改用 xrange if s[i] > t[i]: result.append(t[i]) else: result.append(s[i]) 當然這基本上是 O(n) 所以大方向對了, 不過還是有幾個問題 1. 做了 len(s) 次 index lookups Subscript syntax 基本上是做 random access, 速度不如 iterator 當次數大時效率會有明顯落差 2. 對不等長陣列的支援不好, 如果要加上更多判斷, 可讀性會慢慢下降 3. 出來的東西只能是陣列, 如果你之後還要 iterate 它, 等於做了白工多轉換一次 4. 擴充性不佳, 例如如果以後要多比較一個陣列 r 就會炸裂 針對最後一點, 所以我們必須學習內建的 min 和 max 這裡可以用 min 來改寫 result = [] for i in range(len(s)): # 只給一個參數就預設是從 0 開始 result.append(min(s[i], t[i])) 事實上我覺得即使初學者, 最低限度也要寫成這樣, 更差的都不及格 第三點可以改成 generator 或輸出 iterator 來解決 例如包成 generator... def generate_mins(list1, list2): for i in range(len(list1)): yield min(list1[i], list2[i]) result = generate_mins(s, t) 不過 yield 算進階語法, 為了不用更常用的語法而先學是本末倒置, 所以跳過 另外一種產生方法就等於要學 list comprehension 了 result = (min(s[i], t[i]) for i in range(0, len(s))) 只差在有沒有在這裡直接轉成 list (改用方括弧而非原括弧包起來) 而已 第二點稍後討論, 先講第一點 寫資料處理的程式時, iteration 的速度基本上決定了你的程式價值 即使再怎麼優美或容易維護或怎樣, 你處理速度慢人家超過一定程度就等於垃圾 所以各種語言才會用各式各樣的方法為非連續記憶體的 list loop-through 加速 Python (和其他很多語言) 的做法就是使用 foreach 語法 寫 for i in a_list: i 效率就是會優於 for i in range(a_list): a_list[i] 所以我們要做的是想辦法同時 iterate 複數個 lists, 而不是改用 suubscription Python 提供的解決方案就是 zip 與自動 tuple 展開 所以繼續改寫... result = (min(i[0], i[1]) for i in zip(s, t)) 自動展開 result = (min(a, b) for a, b in zip(s, t)) 這樣就有基本的樣子了 或者 如果你了解到其實 min 其實可以直接吃 iterable... result = (min(i) for i in zip(s, t)) 關於第二點, zip 有一個額外性質是會自動 trim-up 當輸入的 lists 不等長, 會自動把多的丟掉 如果你不喜歡這個行為, 而希望當某個 list 不夠長時提供預設值 則可以使用 itertools module 裡的 izip_longest from itertools import izip_longest result = (min(i) for i in izip_longest(s, t, float('inf'))) float('inf') 會產生「無窮大」, 所以這等於如果有陣列不夠長就無視它 順帶一題, 如果輸入陣列太長, zip 也有可能會有速度問題 這時候可以改用 itertools 的 izip 這樣我們就有了一個效率很好, 可讀性也優良的寫法 大部分人也都是這樣推薦 ====== 前面有人提出 map 的寫法 map(min, zip(s, t, u)) 這也合理, 不過 map 在 Python 3 的行為有改, 要注意 Python 基本上比較不喜歡這類 function-oriented 的語法 這是 Guido 的個人偏好... -- 「我最想要的同伴嘛,首先是要笑口常開,其次是我們能永遠不會發生誤會。 如果這些都能辦到的話,嗯,如果他是世界上第一流的橋手,也還不錯。」 -- 班尼多‧加羅素,前義大利藍隊成員 -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 140.112.94.61

08/01 11:48, , 1F
好文推
08/01 11:48, 1F

08/01 12:08, , 2F
python 的 map 跟 scheme 相同,zip 是不需要的
08/01 12:08, 2F

08/01 12:40, , 3F
受教了
08/01 12:40, 3F

08/01 12:51, , 4F
推這篇!
08/01 12:51, 4F

08/01 12:53, , 5F
這種有歷史跟程式概念的東西要從哪裡學lol
08/01 12:53, 5F

08/01 14:03, , 6F
08/01 14:03, 6F
※ 編輯: uranusjr 來自: 140.112.94.61 (08/01 14:42)

08/01 17:01, , 7F
Push~~
08/01 17:01, 7F

08/01 17:12, , 8F
歷史的部份,有愛就會有阿~
08/01 17:12, 8F

08/01 17:12, , 9F
有愛自然會常常看 Relase Note 跟 PEP, 就會知道很多事情。
08/01 17:12, 9F
文章代碼(AID): #1H-TVm8R (Python)
討論串 (同標題文章)
文章代碼(AID): #1H-TVm8R (Python)