Nice programing

다변량 하스켈 함수를 만드는 방법은 무엇입니까?

nicepro 2020. 11. 4. 08:26
반응형

다변량 하스켈 함수를 만드는 방법은 무엇입니까?


임의의 수의 인수 (모두 동일한 유형)를 가져 와서 작업을 수행 한 후 결과를 반환하는 함수가 필요합니다. 내 특정 경우에는 인수 목록이 실행 불가능합니다.

haskell libs를 살펴 보았을 때, printf(모듈에서 Text.Printf) 함수 가 비슷한 트릭을 사용 한다는 것을 알았습니다 . 안타깝게도 출처를 보면 그 마법을 이해할 수 없었습니다.

누군가 이것을 달성하는 방법을 설명 할 수 있습니까? 또는 적어도 웹 페이지 / 종이 / 이에 대한 좋은 설명을 찾을 수있는 곳은 무엇입니까?

자극:

이것이 필요한 이유는 정말 간단합니다. 학교 (컴퓨터 과학 수업)의 경우 수학적 표현을 "기록"하고이를 문자열로 표현할 수있는 모듈을 작성하고 (자신의 데이터 유형에 대해 Num / Real / etc의 인스턴스를 작성하여) 수행해야합니다. 그것에 대한 다양한 작업.

이 데이터 유형은 변수에 대한 특수 생성자를 포함하며, 값 또는 지정된 함수로 대체 될 수 있습니다. 목표 중 하나는 함수를 작성하는 것입니다.이 함수는 몇 가지 변수 (유형 쌍)가있는 표현식을 가져 와서 표현식 (Char,Rational)의 결과를 계산합니다. 함수의 목표를 가장 잘 표현하는 방법을 살펴보아야합니다. (내 생각 :이 함수는 함수에 정의 된 vars만큼의 인수를받는 또 다른 함수를 반환합니다. 불가능한 것 같습니다.)


의 핵심 printf은 문자열 또는 함수를 반환하는 기능입니다. http://www.haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/src/Text-Printf.html 에서 복사했습니다 .

printf :: (PrintfType r) => String -> r
printf fmts = spr fmts []

class PrintfType t where
    spr :: String -> [UPrintf] -> t

instance (IsChar c) => PrintfType [c] where
    spr fmts args = map fromChar (uprintf fmts (reverse args))

instance (PrintfArg a, PrintfType r) => PrintfType (a -> r) where
    spr fmts args = \a -> spr fmts (toUPrintf a : args)

추출 할 수있는 기본 구조는

variadicFunction :: VariadicReturnClass r => RequiredArgs -> r
variadicFunction reqArgs = variadicImpl reqArgs mempty

class VariadicReturnClass r where
   variadicImpl :: RequiredArgs -> AccumulatingType -> r

instance VariadicReturnClass ActualReturnType where
   variadicImpl reqArgs acc = constructActualResult reqArgs acc

instance (ArgClass a, VariadicReturnClass r) => VariadicReturnClass (a -> r) where
   variadicImpl reqArgs acc = \a -> variadicImpl reqArgs (specialize a `mappend` acc)

예를 들면 :

class SumRes r where 
    sumOf :: Integer -> r

instance SumRes Integer where
    sumOf = id

instance (Integral a, SumRes r) => SumRes (a -> r) where
    sumOf x = sumOf . (x +) . toInteger

다음 우리는 사용할 수 있습니다

*Main> sumOf 1 :: Integer
1
*Main> sumOf 1 4 7 10 :: Integer
22
*Main> sumOf 1 4 7 10 0 0  :: Integer
22
*Main> sumOf 1 4 7 10 2 5 8 22 :: Integer
59

많은 사람들이 가변 함수를 만드는 방법을 알려 주지만이 경우에는 [(Char, Rational)] 유형의 목록을 사용하는 것이 실제로 더 낫다고 생각합니다.


KennyTM의 대답은 훌륭합니다. 다음은 sumOf 1 4 7 10 :: Integer더 나은 그림을 제공하기위한 exec 프로세스의 예 입니다.

sumOf 1 4 7 10
(( \ x -> ( sumOf . (x +) . toInteger ) 1 ) 4 7 10
((sumOf . (1 + ) . toInteger) 4 ) 7 10
( sumOf 5 ) 7 10
( sumOf . (5 + ) . toInteger ) 7 10
sumOf 12 10
sumOf . (12 + ) . toInteger 10
sumof 22
id 22
22

In the wiki article on variadic functions, this article was referenced. I suppose this is what printf does, but I don't understand it either. Anyway, this is certainly an overkill, especially since your arguments are all of the same type. Just put them all in one list. That's what lists are good for - an arbitary number of things of the same type. Fine, it's not very beautiful, but it will hardly be uglier than a complete polyvariadic function.


I took a look at an example linked from the article that delnan referenced. After staring at it for a bit, I think I finally comprehend what is going on:

It starts with this type class:

class BuildList a r  | r-> a where
    build' :: [a] -> a -> r

That bit after the pipe (|) is a functional dependency. It says that the type represented by a can be determined by the type represented by r. In other words, you are prevented from defining two instances of the BuildList typeclass with the same r (return type), but different a.

Jumping ahead a bit to where the build' function is actually used:

> build True :: [Bool]

Since build is just calling build' with an empty list as the first parameter, this is the same as:

> build' [] True :: [Bool]

In this example, build' is clearly returning a list. Because of the functional dependency, we can only be binding to this instance of the BuildList type class:

instance BuildList a [a] where
    build' l x = reverse$ x:l

Pretty straightforward. The second example is more interesting. Expanding the definition of build, it becomes:

> build' [] True False :: [Bool]

What's the type of build' in this case? Well, the precedence rules of Haskell mean that the above could also be written like this:

> (build' [] True) False :: [Bool]

Now it becomes clear that we are passing two parameters to build' and then applying the result of that expression to a parameter with value 'False'. In other words, the expression (build' [] True) is expected to return a function of type Bool -> [Bool]. And that binds us to the second instance of the BuildList typeclass:

instance BuildList a r => BuildList a (a->r) where
    build' l x y = build'(x:l) y

In this invocation, l = [] and x = True and y = False, so the definition expands to build' [True] False :: [Bool]. That signature binds to the first instance of build', and it's fairly obvious where it goes from there.

참고URL : https://stackoverflow.com/questions/3467279/how-to-create-a-polyvariadic-haskell-function

반응형