在Haskell我會寫:
main = do mapM_ print . map (\x -> x^2) . filter (\x -> (mod x 2) == 0) $ [1..20]
在Python我將不得不爲使用許多括號或無用的變量...有沒有像在Python .
和$
什麼?
在Haskell我會寫:
main = do mapM_ print . map (\x -> x^2) . filter (\x -> (mod x 2) == 0) $ [1..20]
在Python我將不得不爲使用許多括號或無用的變量...有沒有像在Python .
和$
什麼?
我只想使用任何慣用的Python的工具可用,如列表內涵,正如其他人指出,而不是試圖假裝你正在寫Haskell,但如果你真的必須,你可以使用compose
combinator函數,即使在Python中:
# this is essentially just foldr (or right `reduce`) specialised on `compose2`
def compose(*args):
ret = identity
for f in reversed(args):
ret = compose2(f, ret)
return ret
def identity(x): return x
def compose2(f, g): return lambda x: f(g(x))
,你可以使用這樣的:
from functools import partial
# equiv. of: map (\x -> x^2) . filter (\x -> (mod x 2) == 0) $ [1..20]
compose(partial(map, lambda x: x**2), partial(filter, lambda x: x % 2 == 0))(range(1, 21))
這固然沒有問題:
>>> compose(partial(map, lambda x: x**2), partial(filter, lambda x: x % 2 == 0))(range(1, 21))
[4, 16, 36, 64, 100, 144, 196, 256, 324, 400]
... 但你可以看到,Python中缺乏某些概念,如鑽營和可以任意定義的中綴運算符,所以即使語義上,上面的代碼片段與Haskell代碼片段是等效的(甚至相同),但它看起來非常糟糕。
至於$
操作:它幾乎沒有相關性在Python - 在Haskell其主要目的是與運算符優先級,這是在Python,因爲你不能真正使用運營商最不成問題無論如何,所有的內置操作符都具有預定義的優先級。
又鑑於$
可以另外在Haskell被用作更高階的函數:
zipWith ($) [(3*), (4+), (5-)] [1,2,3]
...在Python與它(不建議使用)apply
「組合子」複製,這將再次導致代碼只是醜:
>>> list(starmap(apply, zip([lambda x: 3 * x, lambda x: 4 + x, lambda x: 5 - x], map(lambda x: [x], [1, 2, 3]))))
[3, 6, 2]
- 再次,Python中的一些基本限制在起作用這裏:
list()
「強制」starmap,則不會返回「正常」列表;(a -> b) -> a -> b
但(a1 -> a2 -> ... -> aN -> b) -> (a1, a2, ..., aN) -> b
,所以你需要用[]
包裝列表元素,並使用starmap
不正常的map
;這也是缺乏咖喱的結果;map
,reduce
等;(我不熟悉哈斯克爾,但如果我理解你的代碼段這樣正確的...)
您可以使用列表理解來執行過濾和指數。
[i**2 for i in range(1,21) if i%2 == 0]
您也可以通過調整範圍:'range(2,21,2)'來移除'if'後衛。 – chepner
我知道我可以用Python編寫一個簡單的表達式,但是有什麼辦法可以做到這一點(當遇到更復雜的問題時)? –
@Danielhauck有許多圖書館使用非常討厭的黑客來模仿哈斯克爾的風格,但他們都比生產就緒圖書館更有趣的一面。一般來說,你不會得到Haskell的簡潔(儘管你可以把它寫成Haskell中的列表理解爲'[i ** 2 | i < - [2,4..20]]',非常類似於蟒蛇)。一般來說,如果使用迭代器,我可以推薦使用生成器表達式而不是函數組合,但是與Haskell相比,函數組合和部分應用程序在Python中嚴重缺乏。 – bheklilr
由於map函數返回列表,它是迭代和過濾器還可以嵌套他們 -
map(function1, (filter(function2,list)))
欲瞭解更多信息,我建議你閱讀map function documentation也filter function documentation
這就是我的意思是「許多括號」;-) –
是的,只是向可能想要看到此方法的人展示。另外,我認爲這是pythonic :) – kuskmen
對於這種情況你應該更好地使用像@CoryKramer所說的列表理解。
在Python應用部分的應用程序,你應該使用functools.partial
,會是這樣的
from functools import partial
def compose(func1, *func2):
return func1 if not func2 else lambda x: func1(compose(*func2)(x))
myMap = partial(map, lambda x: x**2)
myFilter = partial(filter, lambda x: x%2 == 0)
myFunction = compose(myMap, myFilter)
myFunction(range(20))
諷刺的是(因爲列表內涵是一些Python從象Haskell語言借來的),我可能會同樣把代碼寫在兩種語言:
# Python
for xsquared in [x**2 for x in range(1, 21) if x % 2 == 0]:
print(xsquared)
# legal, but not idiomatic; you don't construct a list just
# to throw it away.
# map(print, [x**2 for x in range(1, 21) if x % 2 == 0])
和
-- Haskell
main = (mapM_ print) [ x^2 | x <- [1..20], x `mod` 2 == 0 ]
或在每一個更簡單:
# Python
for xsquared in [x**2 for x in range(2, 21, 2)]:
print(xsquared)
-- Haskell
main = (mapM_ print) [x^2 | x <- [2,4..20]]
Python中的函數比Haskell更難編寫。 Haskell函數接受一個參數並返回一個值。鑑於f
和g
的定義類型簽名,編譯器很容易檢查f . g
是否有意義。然而,Python沒有這樣的類型簽名(即使在3.5版本中,類型提示也是可選的,只能在靜態分析中使用,而不能在運行時使用)。另外,Python函數可以接受任意數量的參數(不需要currying),而元組的長度是可變的,而不是固定的長度。假設g
返回一個元組。如果f ∘ g
(我個人選擇合成運算符應該採用這樣的事情,並且允許Unicode運算符)等於f(g(...))
或f(*g(...))
?兩者都是有道理的,並且指出兩種不同類型的作品的「需要」。如果g
的返回值f
的值太多或太少,該怎麼辦?關於f
的關鍵字參數呢?他們是否應該從g
返回的字典中提取?在Python中定義一個簡單的操作似乎非常複雜。
另一件我可能完全錯了。我認爲,儘管Python中的每個函數都被編譯爲一段獨特的代碼,但Haskell可以爲每個合成編譯優化的代碼,因此f . g
不僅僅是天真地轉換爲\x -> f (g x)
。至少在Python中,爲
def f(x):
return x + 5
def g(x):
return 3 * x
這是編譯器可以生成f∘g
def fg(x):
return f(g(x))
這將是遠遠低於我的理解Haskell的編譯器可以產生同等效率較低:
def fg(x):
return 3*x + 5
我更喜歡Haskell中的這個:'for_ [2,4..20](print。(^ 2))''。或者,尖尖的風格,'for_ [2,4..20] $ \ x - > print(x^2)'。這強調了該列表被用作「Traversable」而不是「Monad」。 – dfeuer
對於我們這些對Haskell不熟悉的人,你能描述一下這個操作是幹什麼的,可能是用一個輸入/輸出的例子嗎? – CoryKramer
@CoryKramer,'.'允許函數部分應用,'map'和'filter'接收(像在Python中)函數和列表,所以'map(function).filter(function2,list)'意味着'map'應用函數2應用於'list'的元素後,返回'filter'的每個元素的'function'。這是因爲haskell在默認情況下是懶惰的 –
在Haskell中'''是函數組合,大致爲f。 g'大致代表Python'lambda x:f(g(x))'。相反'$'是應用程序'f $ x'只是'f(x)',但允許避免括號。 'f $ x + y + z'表示'f(x + y + z)'。 – chi