2012-11-18 4 views
5

필자는 요소 수나 내용을 모르는 간단한 튜플 (예 : DB에서 읽음)을 사용합니다. 예 : (String, Int, Int) 또는 (String, Float, String, Int)입니다."임의"튜플 조작하기

모든 종류의 튜플을 취하고 모든 데이터를 "NIL"문자열로 바꾸는 일반 함수를 작성하고 싶습니다. "NIL"문자열이 이미있는 경우 변경하지 않아야합니다.

다시 예에 예정 : ("something", 3, 4.788)는 튜플이 할 수있는 문제가되지 않습니다 때문에 어디서부터 시작 내가 분명히 아무 생각이 ("something else", "NIL", "NIL", "NIL")

발생한다 ("something", "NIL", "NIL")

("something else", "Hello", "NIL", (4,6)) 발생한다 그것은 알려져있다. Template Haskell없이 원하는 결과를 얻을 수 있습니까?

+0

첫 번째 항목은 홀로 남기를 원하지만 다른 모든 항목은''NIL ''이 되길 원합니까? – AndrewC

+4

튜플이 올바른 데이터 유형입니까? 이 작업을 수행하는 몇 가지 방법이 있지만 데이터를 더 멋진 유형으로 만들면 훨씬 덜 어색 할 것입니다. 또한 첫 번째 요소에서 이와 같은 특별한 대우를 받기를 원합니까? – shachaf

+0

SYB와 같은 제네릭 라이브러리를 사용하면 Template Haskell보다 더 나은 선택이 될 것입니다. user5402의 대답에 따라 HList를 봐야합니다. –

답변

8

GHC.Generics을 사용하는 것이 가능합니다. 여기서 다른 권장 사항을 권장하지는 않겠지 만 완벽 성을 위해 문서화하겠다고 생각했습니다.

아이디어는 튜플을 패턴 일치가 가능한 것으로 변환하는 것입니다.(HList이 사용하는) 일반적인 방법은 n-tuple을 중첩 된 튜플로 변환하는 것입니다 : (,,,) ->(,(,(,))).

GHC.Generics 튜플을 생성자 :*: 생성자의 중첩 된 응용 프로그램으로 변환하여 비슷한 작업을 수행합니다. tofrom은 값을 일반 표현과 변환하는 함수입니다. 터플 필드는 일반적으로 K1 newtypes로 표현되므로 우리는 K1 리프 노드 (상수)가 발견 될 때까지 메타 데이터 트리 (M1)와 제품 트리 (:*:)를 반복하여 "NIL"문자열.

Rewrite 유형 기능은 유형 수정 방법을 설명합니다. Rewrite (K1 i c) = K1 i String은 각 값 (c 유형 매개 변수)을 String으로 바꿀 것이라고 말합니다.

감안할 때 약간의 테스트 애플 리케이션 :

 
*Main> :main 
("something","NIL","NIL") 
("something else","NIL","NIL","NIL") 

을 내장 Generics 기능과 실제 변환을 할 수있는 typeclass 사용 :

y0 :: (String, Int, Double) 
y0 = ("something", 3, 4.788) 

y1 :: (String, String, String, (Int, Int)) 
y1 = ("something else", "Hello", "NIL", (4,6)) 

main :: IO() 
main = do 
    print (rewrite_ y0 :: (String, String, String)) 
    print (rewrite_ y1 :: (String, String, String, String)) 

우리가 생산하는 일반 라이터를 사용할 수 있습니다

{-# LANGUAGE FlexibleContexts #-} 
{-# LANGUAGE TypeFamilies #-} 
{-# LANGUAGE TypeOperators #-} 

import Data.Typeable 
import GHC.Generics 

rewrite_ 
    :: (Generic a, Generic b, Rewriter (Rep a), Rewrite (Rep a) ~ Rep b) 
    => a -> b 
rewrite_ = to . rewrite False . from 

class Rewriter f where 
    type Rewrite f :: * -> * 
    rewrite :: Bool -> f a -> (Rewrite f) a 

instance Rewriter f => Rewriter (M1 i c f) where 
    type Rewrite (M1 i c f) = M1 i c (Rewrite f) 
    rewrite x = M1 . rewrite x . unM1 

instance Typeable c => Rewriter (K1 i c) where 
    type Rewrite (K1 i c) = K1 i String 
    rewrite False (K1 x) | Just val <- cast x = K1 val 
    rewrite _ _ = K1 "NIL" 

instance (Rewriter a, Rewriter b) => Rewriter (a :*: b) where 
    type Rewrite (a :*: b) = Rewrite a :*: Rewrite b 
    rewrite x (a :*: b) = rewrite x a :*: rewrite True b 

이 예에서 사용하지 않은 몇 가지 인스턴스는 다른 데이터 유형에 대한 필요 : 조금 더 많은 노력으로

instance Rewriter U1 where 
    type Rewrite U1 = U1 
    rewrite _ U1 = U1 

instance (Rewriter a, Rewriter b) => Rewriter (a :+: b) where 
    type Rewrite (a :+: b) = Rewrite a :+: Rewrite b 
    rewrite x (L1 a) = L1 (rewrite x a) 
    rewrite x (R1 b) = R1 (rewrite x b) 

Typeable 제약이 K1 인스턴스에서 제거 할 수 있습니다, 그것은 더 나은 여부로 인해 중복/UndecidableInstances에 논쟁의 여지가있다. GHC는 결과 유형을 추측 할 수도 없지만 그렇게 할 수 있어야합니다. 어쨌든 결과 유형이 정확해야합니다. 그렇지 않으면 읽기 어려운 오류 메시지가 나타납니다.

+0

감사합니다. 나는 이것을 소화해야합니다. 문제는 HList가 아마도 내가 필요한 것이지만 나는()로 표현에 묶여있다. 나는 한때 하스켈에서 이러한 표현을 당신의 취향으로 바꿀 수있는 방법이 있지만 그것을 찾을 수는 없다는 것을 읽었습니다. –

+0

+1, 비슷한 해결책이 있지만 Typeable 대신 OverlappingInstances를 사용했습니다. –

+0

@JFritsch 몇 가지 의견을 추가했습니다. 'GHC.Generics' 자습서 중 하나를 읽는 것이 좋습니다. –

3

모든 튜플은 다른 유형이기 때문에 TH를 사용하지 않고 쉬운 방법으로 함수를 작성할 수는 없다고 생각합니다. 또한 GHC는 허용되는 최대 튜플 크기를 제한합니다. 하스켈 표준은 컴파일러가

Prelude> let a = (1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0) 

<interactive>:2:9: 
    A 70-tuple is too large for GHC 
     (max size is 62) 
     Workaround: use nested tuples or define a data type 

그래서 내가 대신 당신이 다른 데이터 유형을 사용하여 시도해야 튜플을 사용하여 생각

처럼이어야 크기 15 개까지 튜플을 허용해야한다고 말했습니다.

5

튜플 사용에 대한 대안으로 HList 또는 Vinyl 패키지를 살펴보십시오.

비닐은 GHC 7.6이 필요하며 곧 Haskell의 커뮤니티 활동 보고서에 따라 업데이트 될 예정입니다. HList는 특히 SQL 쿼리 결과를 나타내는 데 적합합니다.

HList이 확장 다형성 기록과 변종을 포함하여 입력 이기종 컬렉션을위한 포괄적, 범용 하스켈 라이브러리입니다 : HList 소개

. HList는 표준 목록 라이브러리와 유사하며 다양한 구성, 검색, 필터링 및 반복 프리미티브를 제공합니다. 일반 목록과 달리 이기종 목록의 요소는 동일한 유형을 가질 필요가 없습니다. HList를 사용하면 사용자가 정적으로 확인 가능한 제약 조건을 공식화 할 수 있습니다. 예를 들어 컬렉션의 두 요소가 동일한 유형을 가질 수 없습니다 (요소가 유형별로 명확하게 색인 될 수 있음).

...

HList 라이브러리의 10 월 2012 년 버전 7.4+ GHC에서 제공하는 애호가 유형을 활용하는 중요한 다시 쓰기를 표시합니다. HList는 이제 유형 수준 부울, 자연수 및 목록 및 일종의 다형성에 의존합니다. 여러 가지 연산이 유형 함수로 구현됩니다. 다른 주목할만한 추가 사항은 이기종 목록에 대해 펼쳐집니다. 많은 작업 (투영, 분할)이 펼쳐집니다. 이러한 리팩토링은 런타임 오버 헤드없이 더 많은 계산을 유형 수준으로 옮겼습니다.

4

누군가이 주석에 언급했지만 터플 대신 목록을 사용해야합니다. 당신이 사용

data MyType = S String | D Double 

someData :: [MyType] 

그런 다음 사용 목록을 변환 할 수있는 map 간단한 : 나는 또한 당신이 값 소스의 튜플 크기가 무엇인지 얼마나 이해하지 못하는

convert :: MyType -> String 
convert (S str) = str 
convert _  = "NIL" 

convertList :: [MyType] -> [String] 
convertList = map convert 

. 아마도 그 점을 분명히해야합니다.