2012-03-18 4 views
19

나를 괴롭히는 haskell 플랫폼의 많은 함수를 구현하는 데 매우 일반적인 패턴이 있지만 설명을 찾을 수 없습니다. 그것은 최적화를위한 중첩 된 함수의 사용에 관한 것입니다.하스켈 플랫폼 : 중첩 된 함수와 최적화

꼬리 재귀를 만들기 위해 where 절에서 중첩 된 함수를 사용하는 이유는 매우 명확합니다 (length에서와 같이). 그러나 내부 함수가 최상위 유형과 정확히 동일한 유형 인 경우 용도는 무엇입니까? ?

-- | /O(log n)/. Is the element in the set? 
member :: Ord a => a -> Set a -> Bool 
member = go 
    where 
    STRICT_1_OF_2(go) 
    go _ Tip = False 
    go x (Bin _ y l r) = case compare x y of 
      LT -> go x l 
      GT -> go x r 
      EQ -> True 
#if __GLASGOW_HASKELL__ >= 700 
{-# INLINABLE member #-} 
#else 
{-# INLINE member #-} 
#endif 

내가 그것을 메모이 제이션과 함께 할 수있는 뭔가가 의심,하지만 난 모르겠어요에는 다음과 같은 하나처럼 Data.Set module의 많은 기능에, 예에서 발생합니다.


편집 : 나는 방법이 매크로 작품을 이해

-- Use macros to define strictness of functions. 
-- STRICT_x_OF_y denotes an y-ary function strict in the x-th parameter. 
-- We do not use BangPatterns, because they are not in any standard and we 
-- want the compilers to be compiled by as many compilers as possible. 
#define STRICT_1_OF_2(fn) fn arg _ | arg `seq` False = undefined 

난 아직도하지만, : dave4420가 엄격를 제안하기 때문에, 여기에 같은 모듈에서 찾을 수 있습니다 STRICT_1_OF_2 매크로에 대한 정의가 STRICT_1_OF_2(go)과 함께 gomember 대신 최상위 레벨로 이동되지 않아야하는 이유를 알 수 없습니다.

어쩌면 최적화 때문이 아니라 단순한 문체 선택 때문일 수 있습니다.


편집 2 : 내가 설정 한 모듈에서 INLINABLEINLINE 부분을 추가했다. 나는 그들이 언뜻보기에는 그것과 관련이 있다고 생각하지 않았습니다.

+1

나는 그것이 엄격 성 분석과 관련이 있다고 생각한다 : 컴파일러는'member'에 대한 첫 번째 인수는 평가되어야하지만'go'에 대한 첫 번째 인수는 항상 평가되었을 것으로 추측된다. 그러나 나는 확실하지 않다. – dave4420

+0

@ dave4420 : 의견을 보내 주셔서 감사합니다. 함수의 엄격성에 대한 정보를 더 많이 추가하여 질문을 업데이트했습니다. 이것이 도움이되기를 바랍니다. –

+1

전적으로 문체의 문제라고 생각합니다. 그러나''이동 '에서''x' '를 자유롭게함으로써 약간의 이득을 얻을 수있을 것입니다. x가 모든 반복에서 seq : ed를 얻는다는 것을 암시하는 것처럼 보이기 때문에 이것은 벌이 쓰여진 스타일을 좋아하지 않습니다. 또한 멤버가 'x'일 필요가 없을 때 멤버를 엄격하게 만듭니다 (그러나 사소한 것입니다). – augustss

답변

16

한 즉각적인 이점은 전문입니다. member이 호출 사이트에서 고정 요소 유형으로 정의되는 방식으로 Ord 사전을 삭제하고 적절한 compare 함수를 인라인하거나 적어도 정적 인수로 전달할 수 있습니다.

직접적인 재귀 적 정의에서 member은 루프 브레이커가되어 인라인되지 않으므로 호출 사이트 전문화가 수행되지 않습니다. 즉 적어도 INLINABLEpragma 이전의 이야기였습니다.

INLINABLEpragma를 사용하면 통화 사이트 전문화가 이루어 지지만 사전은 제거되지만 현재의 것보다 효율적으로 직접 재귀적인 member을 작성하지는 못했습니다. 그러나 INLINEpragma를 사용하면 법률은 계속 유지되며 루프 브레이커는 인라인되지 않습니다. member의 경우 사전을 제거하기위한 호출 사이트 전문화가 불가능 함을 의미합니다.

그래서이 스타일로 함수를 작성하는 것은 더 이상 필요하지 않지만 호출 사이트 전문화를 얻으려면 그랬습니다.

13

GHC는 재귀 함수를 인라인 할 수 없습니다. member이 정의 된 방식에서는 재귀가 go으로 제한되고 member 자체는 재귀 적이 아니며 인라인 될 수 있습니다. GHC user guide:

GHC에서

는 인라인 영원히 갈 수 있다는 보장 : 모든 상호 재귀 그룹 이 (인라인없는 GHC의 inliner, JFP의 비밀을 볼 결코 하나 이상의 루프 차단기에 의해 절단 12 (4) 2002 년 7 월). GHC는 INLINE pragma가있는 함수를 루프 분리기로 선택하지 않으려하지만 선택이없는 경우에도 INLINE 함수를 으로 선택할 수 있습니다.이 경우 INLINE pragma는 무시됩니다. 예를 들어 자체 재귀 함수의 경우 루프 차단기는 함수 일 수 있으므로 INLINE pragma는 항상 무시됩니다. 지역 노동자 주위 INLINABLE (또는 INLINE) 래퍼를 갖는

+1

고맙습니다. 이걸로 나는 이해하는 단계에 가깝지만, 아직 이해하지 못합니다. 'INLINE'으로 시작하는 것을 선언 할 시점은 무엇입니까? 모든 것이 아닌 다른 인라인 함수'go'를 호출한다면 무엇입니까? –

+0

그래서 중첩 된 함수를 정의함으로써 우리는 GHC에 루프 차단기로 'go'를 선택할 수있는 기회를줌으로써 어떻게 재귀 함수'member'에 대해서도 인라이닝을 허용합니다. 맞습니까? –

+1

@ 리카 르도, 좋은 지적. http://hackage.haskell.org/trac/ghc/wiki/Inlining에서 몇 가지 관련 정보가 있습니다 아마도이 점이 다소 분명해집니다. –