2015-10-28 42 views
4

在Haskell我會寫:

main = do mapM_ print . map (\x -> x^2) . filter (\x -> (mod x 2) == 0) $ [1..20] 

在Python我將不得不爲使用許多括號或無用的變量...有沒有像在Python .$什麼?

+4

對於我們這些對Haskell不熟悉的人,你能描述一下這個操作是幹什麼的,可能是用一個輸入/輸出的例子嗎? – CoryKramer

+0

@CoryKramer,'.'允許函數部分應用,'map'和'filter'接收(像在Python中)函數和列表,所以'map(function).filter(function2,list)'意味着'map'應用函數2應用於'list'的元素後,返回'filter'的每個元素的'function'。這是因爲haskell在默認情況下是懶惰的 –

+4

在Haskell中'''是函數組合,大致爲f。 g'大致代表Python'lambda x:f(g(x))'。相反'$'是應用程序'f $ x'只是'f(x)',但允許避免括號。 'f $ x + y + z'表示'f(x + y + z)'。 – chi

回答

3

我只想使用任何慣用的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;這也是缺乏咖喱的結果;
  • lambda語法冗長,因爲Guido的個人偏好是針對lambda,mapreduce等;
5

(我不熟悉哈斯克爾,但如果我理解你的代碼段這樣正確的...)

您可以使用列表理解來執行過濾和指數。

[i**2 for i in range(1,21) if i%2 == 0] 
+5

您也可以通過調整範圍:'range(2,21,2)'來移除'if'後衛。 – chepner

+1

我知道我可以用Python編寫一個簡單的表達式,但是有什麼辦法可以做到這一點(當遇到更復雜的問題時)? –

+0

@Danielhauck有許多圖書館使用非常討厭的黑客來模仿哈斯克爾的風格,但他們都比生產就緒圖書館更有趣的一面。一般來說,你不會得到Haskell的簡潔(儘管你可以把它寫成Haskell中的列表理解爲'[i ** 2 | i < - [2,4..20]]',非常類似於蟒蛇)。一般來說,如果使用迭代器,我可以推薦使用生成器表達式而不是函數組合,但是與Haskell相比,函數組合和部分應用程序在Python中嚴重缺乏。 – bheklilr

0

由於map函數返回列表,它是迭代和過濾器還可以嵌套他們 -

map(function1, (filter(function2,list))) 

欲瞭解更多信息,我建議你閱讀map function documentationfilter function documentation

+1

這就是我的意思是「許多括號」;-) –

+0

是的,只是向可能想要看到此方法的人展示。另外,我認爲這是pythonic :) – kuskmen

1

對於這種情況你應該更好地使用像@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)) 
2

諷刺的是(因爲列表內涵是一些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函數接受一個參數並返回一個值。鑑於fg的定義類型簽名,編譯器很容易檢查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 
+0

我更喜歡Haskell中的這個:'for_ [2,4..20](print。(^ 2))''。或者,尖尖的風格,'for_ [2,4..20] $ \ x - > print(x^2)'。這強調了該列表被用作「Traversable」而不是「Monad」。 – dfeuer