2014-05-19 5 views
4

의지도를 하드 코딩하는 가장 효율적인 방법은 무엇입니까. 나는내가 하드 코드 하스켈에 매핑 할 하스켈

message 200 = "OK" 
message 404 = "Not found" 
... 

이지도를 사용

message s = case s of 
    200 -> "OK" 
    404 -> "Not found" 

의 경우 사용 (I 정확한 이름을 rememeber하지 않음) 적어도 3 방법은 그것을

사용하는 여러 정의를 어떻게 볼 수 있습니까? 등

입니다 ...

가장 효율적인 방법이 할 수 있습니까? 하나의 솔루션이 다른 솔루션보다 빠르며 그 이유는 무엇입니까? 처음 두 솔루션은 동등한 솔루션입니까? (컴파일러에서 동일한 코드를 생성합니까?) 권장 방법은 무엇입니까 (읽기 쉬움)?

== 업데이트 == 내 예제에서는 Int을 사용하지만 예제 일뿐입니다. 그것도 String 일 수 있으므로 두 경우 모두에 관심이 있습니다.

+0

'String'은 효율성에 관심이있는 경우 대부분의 경우에 사용할 좋은 유형이 아닙니다. GHC 개발자가 패턴 매칭을위한 특별한 최적화를 작성하려고 시도하지도 않았습니다.'String's - 다른리스트와 같은 방식으로 일치한다고 상상해보십시오. – dfeuer

+0

문자열의 경우,'bytestring'과'bytestring-trie' 패키지를 사용해보십시오. – dfeuer

답변

1

case ... of 상기 다수 방정식 정확히 동일하다. 동일한 코어로 컴파일됩니다. 경우 많은 수의 당신은 아마이 작업을 수행해야합니다

import qualified Data.Map as Map 

message = 
    let 
     theMap = Map.fromList [ (200, "OK"), (404, "Not found"), ... ] 
    in 
     \x -> Map.lookup x theMap 

이 한 번만 맵을 구성한다. 당신이 Maybe String 반환 형식이 마음에 들지 않으면 당신은 결과에 fromMaybe을 적용 할 수 있습니다. 경우 소수 (그들은 정수 특히 경우) 컴파일러는 점프 테이블로 변환 할 수있는 경우 case 문은 아마 빠른 들어

. 이상적인 세계에서는

는 GHC는 자동으로 올바른 버전을 선택합니다. Map의 조회가 ( Data.Map 참조 물론) O(logn) 보장하면서

+0

점프 테이블은 'Bool'과 같은 작은 유형의 경우에는 의미가 있으며 (경우에 따라서는 * 작지도 않음) 경우에 따라 'Word8'과 같은 중형 크기의 경우에도 의미가있을 수 있으며, (즉, 첫 번째 검사 범위, 범위 내에서 점프) 그러나 'Int'와 같은 큰 유형의 임의 값에 대해서는 선형 스캔 (작은 수의 경우) 또는 'Map'과 같은 구현 (많은 경우)이 더 효율적이어야한다고 나는 믿는다. – dfeuer

+1

이 haskell-cafe 스레드는 다음 주제에 대해 다룹니다. https://groups.google.com/d/msg/haskell-cafe/-YTVNbNHHLo/rTWtxwZm8hkJ – ErikR

3

Apparently 함수 패턴 매칭은 O(1) (일정 시간)에 발생한다. 위의 가정을 고려

, 나는 패턴 매칭으로 갈 것 : Int들에

message 200 = "OK" 
message 404 = "Not found" 
+2

선형 복잡성의 증거가 있습니까? 작은 대수 타입의 경우에 매우 놀랍지 만, Int 인 경우에는 그럴 수 있다고 생각합니다. – dfeuer

+0

@ dfeuer, 사실 [it 's constant time] (http://stackoverflow.com/a/9027441/493122). 흥미 롭 군. 그에 따라 답변이 업데이트되었습니다. 확인해 주셔서 고마워요. – Shoe

+2

'Int'에 대한 생성자와 일치하는 것이 생성자에 대한 태그와 일치하는 것과 같은 방식으로 구현되어 있습니까? 링크 된 답변에서 어떤 일이 발생 했습니까? – Cirdec

6

패턴 매칭은지도 조회와 같은 O(log(n)) 시간에 발생합니다.

는 컴파일 된 어셈블리 코드는이 정수의 인수에 이진 검색을하고

.text 
    .align 4,0x90 
    .long _F_f_srt-(_sl8_info)+0 
    .long 0 
    .long 65568 
_sl8_info: 
.Lcma: 
    movl 3(%esi),%eax 
    cmpl $4,%eax 
    jl .Lcmq 
    cmpl $6,%eax 
    jl .Lcmi 
    cmpl $7,%eax 
    jl .Lcme 
    cmpl $7,%eax 
    jne .Lcmc 
    movl $_ghczmprim_GHCziCString_unpackCStringzh_closure,%esi 
    movl $_cm7_str,0(%ebp) 
    jmp _stg_ap_n_fast 
.Lcmc: 
    movl $_ghczmprim_GHCziCString_unpackCStringzh_closure,%esi 
    movl $_clB_str,0(%ebp) 
    jmp _stg_ap_n_fast 
.Lcme: 
    cmpl $6,%eax 
    jne .Lcmc 
    movl $_ghczmprim_GHCziCString_unpackCStringzh_closure,%esi 
    movl $_cm3_str,0(%ebp) 
    jmp _stg_ap_n_fast 
.Lcmg: 
    cmpl $4,%eax 
    jne .Lcmc 
    movl $_ghczmprim_GHCziCString_unpackCStringzh_closure,%esi 
    movl $_clV_str,0(%ebp) 
    jmp _stg_ap_n_fast 
.Lcmi: 
    cmpl $5,%eax 
    jl .Lcmg 
    cmpl $5,%eax 
    jne .Lcmc 
    movl $_ghczmprim_GHCziCString_unpackCStringzh_closure,%esi 
    movl $_clZ_str,0(%ebp) 
    jmp _stg_ap_n_fast 
.Lcmk: 
    cmpl $2,%eax 
    jne .Lcmc 
    movl $_ghczmprim_GHCziCString_unpackCStringzh_closure,%esi 
    movl $_clN_str,0(%ebp) 
    jmp _stg_ap_n_fast 
.Lcmm: 
    testl %eax,%eax 
    jne .Lcmc 
    movl $_ghczmprim_GHCziCString_unpackCStringzh_closure,%esi 
    movl $_clF_str,0(%ebp) 
    jmp _stg_ap_n_fast 
.Lcmo: 
    cmpl $1,%eax 
    jl .Lcmm 
    cmpl $1,%eax 
    jne .Lcmc 
    movl $_ghczmprim_GHCziCString_unpackCStringzh_closure,%esi 
    movl $_clJ_str,0(%ebp) 
    jmp _stg_ap_n_fast 
.Lcmq: 
    cmpl $2,%eax 
    jl .Lcmo 
    cmpl $3,%eax 
    jl .Lcmk 
    cmpl $3,%eax 
    jne .Lcmc 
    movl $_ghczmprim_GHCziCString_unpackCStringzh_closure,%esi 
    movl $_clR_str,0(%ebp) 
    jmp _stg_ap_n_fast 
.text 
    .align 4,0x90 
    .long _F_f_srt-(_F_f_info)+0 
    .long 65541 
    .long 0 
    .long 65551 
.globl _F_f_info 
_F_f_info: 
.Lcmu: 
    movl 0(%ebp),%esi 
    movl $_sl8_info,0(%ebp) 
    testl $3,%esi 
    jne .Lcmx 
    jmp *(%esi) 
.Lcmx: 
    jmp _sl8_info 

입니다 ghc -S

module F (
    f 
) where 

f :: Int -> String 
f 0 = "Zero" 
f 1 = "One" 
f 2 = "Two" 
f 3 = "Three" 
f 4 = "Four" 
f 5 = "Five" 
f 6 = "Six" 
f 7 = "Seven" 
f _ = "Undefined" 

에 의해 86 어셈블리로 컴파일 다음 코드를 살펴 보자. .Lcma 그 < 1에있는 지점, .Lcmo 간다로부터 제 2 비교 < 다음 < 3. 분기되는 < 6 다음 < 7. 제 comparsion가 .Lcmq 간다 < 다음 4 분파된다.대신 우리는 우리가 같은 패턴을 볼 수있는 곳을 얻을 ghc -O2 -S

:

.text 
    .align 4,0x90 
    .long _F_zdwf_srt-(_F_zdwf_info)+0 
    .long 65540 
    .long 0 
    .long 33488911 
.globl _F_zdwf_info 
_F_zdwf_info: 
.LcqO: 
    movl 0(%ebp),%eax 
    cmpl $4,%eax 
    jl .Lcr6 
    cmpl $6,%eax 
    jl .LcqY 
    cmpl $7,%eax 
    jl .LcqU 
    cmpl $7,%eax 
    jne .LcqS 
    movl $_F_f1_closure,%esi 
    addl $4,%ebp 
    andl $-4,%esi 
    jmp *(%esi) 
.LcqS: 
    movl $_F_f9_closure,%esi 
    addl $4,%ebp 
    andl $-4,%esi 
    jmp *(%esi) 
.LcqU: 
    cmpl $6,%eax 
    jne .LcqS 
    movl $_F_f2_closure,%esi 
    addl $4,%ebp 
    andl $-4,%esi 
    jmp *(%esi) 
.LcqW: 
    cmpl $4,%eax 
    jne .LcqS 
    movl $_F_f4_closure,%esi 
    addl $4,%ebp 
    andl $-4,%esi 
    jmp *(%esi) 
.LcqY: 
    cmpl $5,%eax 
    jl .LcqW 
    cmpl $5,%eax 
    jne .LcqS 
    movl $_F_f3_closure,%esi 
    addl $4,%ebp 
    andl $-4,%esi 
    jmp *(%esi) 
.Lcr0: 
    cmpl $2,%eax 
    jne .LcqS 
    movl $_F_f6_closure,%esi 
    addl $4,%ebp 
    andl $-4,%esi 
    jmp *(%esi) 
.Lcr2: 
    testl %eax,%eax 
    jne .LcqS 
    movl $_F_f8_closure,%esi 
    addl $4,%ebp 
    andl $-4,%esi 
    jmp *(%esi) 
.Lcr4: 
    cmpl $1,%eax 
    jl .Lcr2 
    cmpl $1,%eax 
    jne .LcqS 
    movl $_F_f7_closure,%esi 
    addl $4,%ebp 
    andl $-4,%esi 
    jmp *(%esi) 
.Lcr6: 
    cmpl $2,%eax 
    jl .Lcr4 
    cmpl $3,%eax 
    jl .Lcr0 
    cmpl $3,%eax 
    jne .LcqS 
    movl $_F_f5_closure,%esi 
    addl $4,%ebp 
    andl $-4,%esi 
    jmp *(%esi) 
.section .data 
    .align 4 
.align 1 
_F_f_srt: 
    .long _F_zdwf_closure 
.data 
    .align 4 
.align 1 
.globl _F_f_closure 
_F_f_closure: 
    .long _F_f_info 
    .long 0 
.text 
    .align 4,0x90 
    .long _F_f_srt-(_srh_info)+0 
    .long 0 
    .long 65568 
_srh_info: 
.Lcrv: 
    movl 3(%esi),%eax 
    movl %eax,0(%ebp) 
    jmp _F_zdwf_info 
.text 
    .align 4,0x90 
    .long _F_f_srt-(_F_f_info)+0 
    .long 65541 
    .long 0 
    .long 65551 
.globl _F_f_info 
_F_f_info: 
.Lcrz: 
    movl 0(%ebp),%esi 
    movl $_srh_info,0(%ebp) 
    testl $3,%esi 
    jne _srh_info 
    jmp *(%esi) 

우리는 가지 < 5에있는

f :: Int -> String 
f 1 = "Zero" 
f 2 = "One" 
f 3 = "Two" 
f 4 = "Three" 
f 5 = "Four" 
f 6 = "Five" 
f 7 = "Six" 
f 8 = "Seven" 
f _ = "Undefined" 

, < 7 원래의 코드를 변경하는 경우 , < 8, < 5가 < 3, < 4 등이 될 수 있습니다. 따라서 인수를 정렬하면이 작업을 수행 할 수 있습니다. , < 30 < (50)는가는, 가지가, < (50)에 여전히 < 70, < 80입니다

f :: Int -> String 
f 20 = "Zero" 
f 80 = "One" 
f 70 = "Two" 
f 30 = "Three" 
f 40 = "Four" 
f 50 = "Five" 
f 10 = "Six" 
f 60 = "Seven" 
f _ = "Undefined" 

과연 < 40 : 우리는 숫자를 스크램블링, 심지어 그들 사이에 간격을 추가하여 그것을 테스트 할 수 있습니다 등등.

+0

ADT를'-O2'와 함께 사용하면 점프 테이블을 얻을 수 있다고 생각합니다. 또한이 haskell-cafe 스레드를 참조하십시오. https://groups.google.com/d/msg/haskell-cafe/-YTVNbNHHLo/rTWtxwZm8hkJ – ErikR

+0

제 질문은 Int. 내 업데이트보기 – mb14