2016-07-28 3 views
5

this answer on CodeReview에서 asker와 answerer는 (++) 연산자에 대해 경멸감을 보입니다. 이것은 속도 때문입니다 (알고리즘을 O (n^2)에서 명시 적으로 실행시키는 원인이됩니까? n은 목록 iirc의 길이입니다)? Haskell이 시간 복잡성에 대해 추론하기 어렵다는 것이 알려져 있기 때문에 다른 테스트를 거치지 않았다면 이것은 사전 최적화입니까? 다른 사람들이 프로그램에서 (++) 연산자를 사용하지 않아야합니까?하스켈에서 ++ 피하기

+0

하스켈 목록은 링크 된 목록이고 추가 목록은 엄격한 언어의 O (* n *) 연산입니다. 그러나 Haskell에서는 게으름으로 목록이 사용될 때까지 작업을 연기 할 수 있으므로 목록의 길이에 걸쳐 효과적으로 비용을 분배 할 수 있습니다. 이 게으름은 하스켈에서 시간과 공간의 복잡성을 설명하기 어렵게 만들지 만, 다른 언어에 대한 동일한 조언이 하스켈에도 적용된다고 생각합니다. 먼저 명확성을 기하고, 둘 모두가 필요한 경우에만 속도를 최적화하십시오. 더 빨리 갈 수 있습니다 * 그리고 * 병목 현상을 발견하기 위해 프로파일 링했습니다. –

+0

두 가지 모두 있습니다. 코드에 대해 추론하기가 더 어려워지며 대개 코드가 느려지고 사용하는 경우가 많습니다. 그러나 모든 상황과 IMO에 따라 다릅니다. SO – Carsten

+0

@Carsten : 나는 보통 "당신의 코드 속도가 느려진다"라는 당신의 말에 약간 혼란 스럽다. 이것은 정규 tail-recursive 계승의 예에서 분명히 사실이지만, 이것은 일상적인 프로그램에서 실제로 아주 흔한 일입니까? 루비 (Ruby) 나 파이썬 (Python)과 같이 널리 사용되는 외부인 언어 중 하나를 사용하는 하스켈 (Haskell)과 같은 이점이 풍부한 타이핑 시스템에 의해 제공되는 최적화라고 생각했습니다. 정말로 내 질문이 구체적이지 않다고 생각하는 경우, 제공된 예에서 마지막 질문 만 저장하면됩니다 (이는 내 의도 였고 ThreeFx가 알아 냈다고 생각합니다.) –

답변

9

에 따라 다릅니다.

이 표현식은 하나의리스트에리스트 목록을 연결하지만, 상기 이차 복잡도를 갖는 표현식을

foldl (++) [] list 

을 고려한다. 이것은 (++)의 구현이 전체 왼쪽 목록을 가로 지르고 올바른 순서로 각 요소를 오른쪽 목록 앞에 추가하기 때문에 발생합니다.

foldr (++) [] list 

이는 왼쪽 인자를 통과하고 오른쪽으로 앞에 추가 (++) 운영자의 구현에 기인한다 : 오른쪽 배를 사용

, 우리는 선형 복잡도를 얻을.

[1,2] ++ [3,4] ++ [5,6] 

번만 각리스트 요소를 통과

-- Example as created by foldr 
[1,2] ++ ([3,4] ++ [5,6]) 
== [1,2] ++ [3,4,5,6] 
== [1,2,3,4,5,6] -- all good, no element traversed more than once 

같다.

이제 괄호를 처음 두 목록으로 전환하는 것이 더 비쌉니다. 이제 일부 요소가 여러 번 통과하기 때문에 비효율적입니다.

-- Example as created by foldl 
([1,2] ++ [3,4]) ++ [5,6] 
== [1,2,3,4] ++ [5,6] 
== [1,2,3,4,5,6] -- the sublist [1,2] was traversed twice due to the ordering of the appends 

이러한 경우에는주의해야합니다.

+0

foldl이 항상 왼쪽 연관 연산자와 함께 사용되도록 일반화 할 수 있습니까? 우리가 foldl로 무엇을합니까? 편집 : 작업이 왼쪽과 오른쪽 모두 연관되어 있으면 foldl을 의미합니까 == foldr ? 그리고 어떤 상황에서 foldl ! = foldr ? 하스켈은 실제로 이러한 병목 현상을 인식하지 못합니까? 나는 컴파일러가 (특히 GHC를 언급하면서) 세계 최고가 될 것으로 생각했다. 최적화하지 않기 위해 어리석은 것처럼 보인다. (비록 내 리그에서 빠져 나올지 모르지만 ...). –

+0

@JackBrandt 아니요, 적어도 일반적인 규칙은 아닙니다. '(^)'(또는'(-)')를 상상해보십시오.이 연산자는 왼쪽과 오른쪽 접힌 상태에서 각각 다른 결과를냅니다. 그것은 당신이 원하는 것을 따라 달라집니다. 'foldl '은 완전히 다른 문제입니다. 표준의 엄격한 버전 인'foldl'이며, 명시 적으로 썽크를 만들고 싶을 때를 제외하고는'foldl'보다 거의 항상 선호되어야합니다. – ThreeFx

+0

누군가가 썽크를 키우고 싶은 이유를 확장 할 수 있습니까? 대중적인 이해에 오히려 직관력이없는 것처럼 보입니다. 또한, 수학적 예제는 실제로 b/t 폴드의 차이를 이해하는 데 매우 유용했습니다. –