간단한 '평균'함수를 좌절시키는 Haskell 유형
저는 초보자 Haskell과 놀면서 평균적인 함수를 작성하고 싶었습니다. 세상에서 가장 단순한 것 같았 죠?
잘못된.
Haskell의 유형 시스템이 일반 숫자 유형에 대한 작업을 평균하는 것을 금지하는 것처럼 보입니다. Integrals 또는 Fractionals 목록에서 작동하도록 할 수 있지만 둘다는 아닙니다.
내가 원하는:
average :: (Num a, Fractional b) => [a] -> b
average xs = ...
그러나 나는 다음을 얻을 수 있습니다.
averageInt :: (Integral a, Fractional b) => [a] -> b
averageInt xs = fromIntegral (sum xs) / fromIntegral (length xs)
또는
averageFrac :: (Fractional a) => [a] -> a
averageFrac xs = sum xs / fromIntegral (length xs)
두 번째는 작동하는 것 같습니다. 변수를 전달하려고 할 때까지.
*Main> averageFrac [1,2,3]
2.0
*Main> let x = [1,2,3]
*Main> :t x
x :: [Integer]
*Main> averageFrac x
<interactive>:1:0:
No instance for (Fractional Integer)
arising from a use of `averageFrac ' at <interactive>:1:0-8
Possible fix: add an instance declaration for (Fractional Integer)
In the expression: average x
In the definition of `it': it = averageFrac x
분명히 Haskell은 그 유형에 대해 정말 까다 롭습니다. 말이 되네요. 그러나 둘 다 [Num]이 될 수있는 경우는 아닙니다.
RealFrac의 명백한 응용 프로그램이 누락 되었습니까?
분수 입력을받을 때 질식하지 않는 분수로 적분을 강제 변환하는 방법이 있습니까?
모든 종류의 숫자 배열에서 작동하는 일종의 다형성 평균 함수 를 사용 Either하고 either만드는 방법이 있습니까?
Haskell의 유형 시스템이이 함수가 존재하지 않도록 완전히 금지합니까?
Haskell을 배우는 것은 미적분을 배우는 것과 같습니다. 그것은 정말 복잡하고 산더미 같은 이론을 기반으로하고 있으며 때로는 문제가 너무 복잡해서 질문을 올바르게 표현할만큼 충분히 알지 못하기 때문에 어떤 통찰력도 따뜻하게 받아 들여질 것입니다.
(또한 각주 : 이것은 숙제 문제를 기반으로합니다. 위의 averageFrac이 전체 점수를 얻는다는 데 모두 동의하지만 Integral 및 Fractional 배열 모두에서 작동하도록 만드는 방법이 있다는 의심이 있습니다)
따라서 기본적으로 (/) 유형에 의해 제한됩니다.
(/) :: (Fractional a) => a -> a -> a
BTW, Data.List.genericLength도 원합니다.
genericLength :: (Num i) => [b] -> i
따라서 더 일반적인 것을 위해 fromIntegral을 제거하는 것은 어떻습니까?
import Data.List
average xs = realToFrac (sum xs) / genericLength xs
Real 제약 조건 (Int, Integer, Float, Double) 만 있습니다.
average :: (Real a, Fractional b) => [a] -> b
그래서 그것은 모든 실수를 분수로 가져갈 것입니다.
그리고 모든 포스터가 Haskell의 다형성 숫자 리터럴에 의해 잡히는 것을 주목하십시오. 1은 정수가 아니고 임의의 숫자입니다.
Real 클래스는 Num 클래스의 값을 합리적으로 바꾸는 기능 만 제공합니다. 정확히 우리가 여기에서 필요한 것입니다.
따라서,
Prelude> average ([1 .. 10] :: [Double])
5.5
Prelude> average ([1 .. 10] :: [Int])
5.5
Prelude> average ([1 .. 10] :: [Float])
5.5
Prelude> average ([1 .. 10] :: [Data.Word.Word8])
5.5
Dons는이 질문에 대해 매우 잘 대답했습니다. 제가 뭔가를 추가 할 수 있다고 생각했습니다.
이 방법으로 평균을 계산할 때 :
average xs = realToFrac (sum xs) / genericLength xs
코드가 할 일은 목록을 두 번, 한 번은 요소의 합계를 계산하고 한 번은 길이를 가져 오는 것입니다. 내가 아는 한, GHC는 아직 이것을 최적화 할 수 없으며 단일 패스에서 합계와 길이를 모두 계산할 수 없습니다.
It doesn't hurt even as a beginner to think about it and about possible solutions, for example the average function might be written using a fold that computes both the sum and length; on ghci :
:set -XBangPatterns
import Data.List
let avg l=let (t,n) = foldl' (\(!b,!c) a -> (a+b,c+1)) (0,0) l in realToFrac(t)/realToFrac(n)
avg ([1,2,3,4]::[Int])
2.5
avg ([1,2,3,4]::[Double])
2.5
The function doesn't look as elegant, but the performance is better.
More information on Dons blog:
Since dons has done such a good job at answering your question, I'll work on questioning your question....
For example, in your question, where you first run an average on a given list, getting a good answer. Then, you take what looks like the exact same list, assign it to a variable, then use the function the variable...which then blows up.
What you've run into here is a set-up in the compiler, called the DMR: the D readed M onomorphic R estriction. When you passed the list straight into the function, the compiler made no assumption about which type the numbers were, it just inferred what types it could be based on usage, and then picked one once it couldn't narrow the field down any more. It's kind of like the direct opposite of duck-typing, there.
Anyway, when you assigned the list to a variable, the DMR kicked in. Since you've put the list in a variable, but given no hints on how you want to use it, the DMR made the compiler pick a type, in this case, it picked one that matched the form and seemed to fit: Integer. Since your function couldn't use an Integer in its / operation (it needs a type in the Fractional class), it makes that very complaint: there's no instance of Integer in the Fractional class. There are options you can set in GHC so that it doesn't force your values into a single form ("mono-morphic", get it?) until it needs to, but it makes any error messages slightly tougher to figure out.
Now, on another note, you had a reply to dons' answer that caught my eye:
I was mislead by the chart on the last page of cs.ut.ee/~varmo/MFP2004/PreludeTour.pdf that shows Floating NOT inheriting properties from Real, and I then assumed that they would share no types in common.
Haskell does types differently from what you're used to. Real and Floating are type classes, which work more like interfaces than object classes. They tell you what you can do with a type that's in that class, but it doesn't mean that some type can't do other things, any more than having one interface means that a(n OO-style) class can't have any others.
Learning Haskell is like learning Calculus
I'd say learning Haskell is like learning Swedish - there are lots of little, simple things (letters, numbers) that look and work the same, but there are also words that look like they should mean one thing, when they actually mean something else. But once you get fluent in it, your regular friends will be amazed at how you can spout off this oddball stuff that makes gorgeous beauties do amazing tricks. Curiously, there are many folks involved in Haskell from the beginnings, who also know Swedish. Maybe that metaphor is more than just a metaphor...
:m Data.List
let list = [1..10]
let average = div (sum list) (genericLength list)
average
Yeah, Haskell's type system is very picky. The problem here is the type of fromIntegral:
Prelude> :t fromIntegral
fromIntegral :: (Integral a, Num b) => a -> b
fromIntegral will only accept an Integral as a, not any other kind of Num. (/), on the other hand only accepts fractional. How do you go about making the two work together?
Well, the sum function is a good start:
Prelude> :t sum
sum :: (Num a) => [a] -> a
Sum takes a list of any Num and returns a Num.
Your next problem is the length of the list. The length is an Int:
Prelude> :t length
length :: [a] -> Int
You need to convert that Int into a Num as well. That's what fromIntegral does.
So now you've got a function that returns a Num and another function that returns a Num. There are some rules for type promotion of numbers you can look up, but basically at this point you're good to go:
Prelude> let average xs = (sum xs) / (fromIntegral (length xs))
Prelude> :t average
average :: (Fractional a) => [a] -> a
Let's give it a trial run:
Prelude> average [1,2,3,4,5]
3.0
Prelude> average [1.2,3.4,5.6,7.8,9.0]
5.4
Prelude> average [1.2,3,4.5,6,7.8,9]
5.25
참고URL : https://stackoverflow.com/questions/2376981/haskell-types-frustrating-a-simple-average-function
'program story' 카테고리의 다른 글
| REST API에서 JSON을 반환하는 경우 어떤 MIME 유형입니까? (0) | 2020.11.11 |
|---|---|
| Java에서 길이를 알 수없는 바이트 배열 (0) | 2020.11.11 |
| 쉘 스크립트 헤더 (#! / bin / sh 대 #! / bin / csh) (0) | 2020.11.11 |
| SimpleDateFormat 클래스에서 사용할 수있는 날짜 형식은 무엇입니까? (0) | 2020.11.10 |
| 문자 뒤의 문자열 가져 오기 (0) | 2020.11.10 |