해석 된 산술


9

약간의 알려진 사실은 충분한 언어 확장 (ghc)을 켜면 Haskell이 동적으로 형식화 된 해석 언어가된다는 것입니다! 예를 들어 다음 프로그램은 추가를 구현합니다.

{-# Language MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, UndecidableInstances #-}

data Zero
data Succ a

class Add a b c | a b -> c
instance Add Zero a a
instance (Add a b c) => Add (Succ a) b (Succ c)

이것은 더 이상 Haskell처럼 보이지 않습니다. 객체를 조작하는 대신 유형을 조작합니다. 각 번호는 고유 한 유형입니다. 함수 대신에 타입 클래스가 있습니다. 기능적 종속성은 유형 간 함수로 사용할 수 있습니다.

그렇다면 코드를 어떻게 호출합니까? 우리는 다른 수업을 사용합니다

class Test a | -> a
 where test :: a
instance (Add (Succ (Succ (Succ (Succ Zero)))) (Succ (Succ (Succ Zero))) a)
  => Test a

이것은 타입을 test타입 4 + 3으로 설정합니다. 만약 이것을 ghci로 열면 test실제로 타입 7이라는 것을 알 수 있습니다 .

Ok, one module loaded.
*Main> :t test
test :: Succ (Succ (Succ (Succ (Succ (Succ (Succ Zero))))))

직무

두 Peano 숫자 (음이 아닌 정수)를 곱하는 클래스를 구현하고 싶습니다. Peano 숫자는 위의 예에서 동일한 데이터 유형을 사용하여 구성됩니다.

data Zero
data Succ a

그리고 당신의 수업은 위와 같은 방식으로 평가 될 것입니다. 원하는대로 수업 이름을 지정할 수 있습니다.

바이트 단위로 무료로 원하는 ghc 언어 확장을 사용할 수 있습니다.

테스트 사례

이 테스트 사례는 클래스의 이름이 M이고 원하는 경우 다른 이름을 지정할 수 있습니다.

class Test1 a| ->a where test1::a
instance (M (Succ (Succ (Succ (Succ Zero)))) (Succ (Succ (Succ Zero))) a)=>Test1 a

class Test2 a| ->a where test2::a
instance (M Zero (Succ (Succ Zero)) a)=>Test2 a

class Test3 a| ->a where test3::a
instance (M (Succ (Succ (Succ (Succ Zero)))) (Succ Zero) a)=>Test3 a

class Test4 a| ->a where test4::a
instance (M (Succ (Succ (Succ (Succ (Succ (Succ Zero)))))) (Succ (Succ (Succ Zero))) a)=>Test4 a

결과

*Main> :t test1
test1
  :: Succ
       (Succ
          (Succ
             (Succ
                (Succ (Succ (Succ (Succ (Succ (Succ (Succ (Succ Zero)))))))))))
*Main> :t test2
test2 :: Zero
*Main> :t test3
test3 :: Succ (Succ (Succ (Succ Zero)))
*Main> :t test4
test4
  :: Succ
       (Succ
          (Succ
             (Succ
                (Succ
                   (Succ
                      (Succ
                         (Succ
                            (Succ
                               (Succ
                                  (Succ
                                     (Succ (Succ (Succ (Succ (Succ (Succ (Succ Zero)))))))))))))))))

기술 인터뷰 입력에서 영감을 얻습니다


언어 확장은 무료입니까? 그렇다면 어느 것입니까?
감자 44

@ Potato44 아 그래. 모든 언어 확장은 무료입니다.
Ad Hoc Garf Hunter

1
Heh ...이 게시물은 비록 그렇지 않더라도 meme-y 인 것 같습니다.
Magic Octopus Urn

답변:


9

130 121 바이트

Ørjan Johansen 덕분에 -9 바이트

type family a+b where s a+b=a+s b;z+b=b
type family a*b where s a*b=a*b+b;z*b=z
class(a#b)c|a b->c
instance a*b~c=>(a#b)c

온라인으로 사용해보십시오!

이것은 덧셈 (+)과 곱셈을 위한 닫힌 타입 패밀리를 정의합니다 (*). 그런 다음 유형 패밀리를 동등 구속 조건과 함께 (#)사용하여 (*)패밀리 유형의 세계에서 유형 클래스 프롤로그의 세계로 변환합니다.


3
당신이 방정식을 교체하는 경우 교체 할 수 있습니다 Zeroz.
Ørjan Johansen 2016 년

1
@ ØrjanJohansen 님. 나는 누군가를 위해 9 바이트를 저장하고 9 바이트는 나를 위해 저장됩니다.
감자 44

나는 형의 가족을 사용하는 방법을 모르지만, 어쩌면 같은 기능 사용자가 정의 할 필요가 없습니다이 +유용하다?
Lynn

@Lynn은 결국 더 길게 나옵니다. TIO
Potato44

1
@WheatWizard 방금 주석에 게시 한 코드가 기본적으로 답변의 꼬리 재귀 버전이라는 것을 깨달았습니다.
감자 44 1

6

139 바이트

class(a+b)c|a b->c;instance(Zero+a)a;instance(a+b)c=>(s a+b)(s c)
class(a*b)c|a b->c;instance(Zero*a)Zero;instance((a*b)c,(b+c)d)=>(s a*b)d

온라인으로 사용해보십시오!

타입 연산자를 정의합니다 *. 프롤로그 프로그램과 동일합니다.

plus(0, A, A).
plus(s(A), B, s(C)) :- plus(A, B, C).
mult(0, _, 0).
mult(s(A), B, D) :- mult(A, B, C), plus(B, C, D).

Potato44와 Hat Wizard는 각각 9 바이트를 절약했습니다. 감사!


데이터 선언을 총 바이트 수로 계산할 필요는 없습니다. 내가 기회를 얻을 때 나는 질문이 명확를 만들거야
임시 Garf 헌터

또한 나는 f대신에 일반을 사용할 수 있다고 생각합니다 Succ.
Ad Hoc Garf Hunter

1
콜론을 제거하여 9 바이트를 절약 할 수 있습니다.
감자 44

나는 모자 마법사가 6이 아닌 9를 구했다고 생각한다.
감자 44

1

패밀리 버전, 115 바이트

type family(a%b)c where(a%b)(s c)=s((a%b)c);(s a%b)z=(a%b)b;(z%b)z=z
class(a#b)c|a b->c
instance(a%b)Zero~c=>(a#b)c

온라인으로 사용해보십시오!

이것은 potato44 's 와 같은 폐쇄 형 패밀리를 사용합니다 . 다른 답변과 달리 나는 1 가지 유형의 가족 만 사용합니다.

type family(a%b)c where
  -- If the accumulator is Succ c:
  -- the answer is Succ of the answer if the accumulator were c
  (a%b)(s c)=s((a%b)c)
  -- If the left hand argument is Succ a, and the right hand is b
  -- the result is the result if the left were a and the accumulator were b
  (s a%b)z=(a%b)b
  -- If the left hand argument is zero
  -- the result is zero
  (z%b)z=z

이것은 세 가지 유형의 연산자를 정의합니다. 본질적으로 구현 (a*b)+c합니다. 우리는 오른손 인수를 총계에 추가하고 싶을 때마다 대신 누적기에 넣습니다.

이것은 우리가 (+)전혀 정의 할 필요가 없도록 합니다. 기술적으로이 제품군을 사용하여 추가를 구현할 수 있습니다.

class Add a b c | a b -> c
instance (Succ Zero % a) b ~ c => Add a b c

클래스 버전, 137 바이트

class(a#b)c d|a b c->d
instance(a#b)c d=>(a#b)(f c)(f d)
instance(a#b)b d=>(f a#b)Zero d
instance(Zero#a)Zero Zero
type(a*b)c=(a#b)Zero c

온라인으로 사용해보십시오!

이 클래스 버전은 패밀리 버전과 약간 차이가 있지만 여기서는 가장 짧은 클래스 버전보다 짧습니다. 그것은 내 가족 버전과 같은 접근법을 사용합니다.


좋아, 나는 당신의 타입 패밀리가 수학적으로 a * b + c를 구현하고 있음을 알았습니다. "분할"에 대한 언급이 "추가"라는 의미입니까?
감자 44 nov

btw, 당신은 현재 자신의 사양을 위반하고 있습니다. "두 Peano 숫자를 곱한 클래스 구현"현재 클래스가 아닌 것은 종류가 있습니다 Constraint. 따라서 스펙을 업데이트하거나 유형 동의어 대신 클래스를 사용하는 양식으로 되돌려 야합니다. 타입 동의어를 사용한다면 96 바이트로 답을 얻을 수있어서, 당신보다 한 바이트 더 절약 할 수 있습니다.
Potato44

@ Potato44 나는 수업이 단지 일종의 금기 사항이라는 느낌을 받았습니다. 아마도 그 질문에 명확성이 부족하기 때문일 것입니다. 그런 다음 115 답변으로 되돌아갑니다.
Ad Hoc Garf Hunter
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.