프로그래밍에서 "대수"는 무엇을 의미합니까?


339

함수형 프로그래밍과 PLT 서클에서 "대수학"이라는 용어를 여러 번 들었습니다. 특히 토론이 객체, 코 모나드, 렌즈 등에 관한 것입니다. 이 용어를 인터넷으로 검색하면 이러한 구조에 대한 수학적 설명을 제공하는 페이지가 나와 있으며, 이는 거의 이해할 수없는 내용입니다. 프로그래밍 맥락에서 대수학이 무엇을 의미하는지, 그 의미가 무엇이며, 객체와 코모 나와 어떻게 관련되는지 설명해 주시겠습니까?


21
FP에서 Jeremy Gibbons의 훌륭한 책 Patterns : patternsinfp.wordpress.com 과 그의 매우 이해하기 쉬운 "계산 프로그램 계산하기"를 추천 할 수 있습니까? 그들은 둘 다 꽤 엄격한 방식으로 (예를 들어 블로그 게시물과 비교하여) 대수를 다루지 하스켈을 조금 아는 사람에게는 상당히 독립적입니다.
Kristopher Micinski

2
@KristopherMicinski, 매우 흥미로운. 감사!
missingfaktor

답변:


474

대수

시작해야 할 곳은 대수학 의 개념을 이해하는 것이라고 생각합니다 . 이것은 그룹, 고리, 모노 이드 등과 같은 대수 구조의 일반화입니다. 대부분의 경우, 이러한 것들은 세트의 관점에서 소개되지만, 우리가 친구 사이이므로 Haskell 유형에 대해 이야기하겠습니다. (하지만 그리스 문자 사용을 거부 할 수는 없습니다. 모든 것이 더 시원해 보입니다!)

그러면 대수는 τ일부 기능과 정체성을 가진 유형일뿐 입니다. 이 함수는 다른 수의 인수를 τ사용하여 τ: curried를 생성합니다 (τ, τ,…, τ) → τ. 또한 τ일부 기능에서 특별한 동작 을 하는 요소 인 "정체성"을 가질 수 있습니다 .

가장 간단한 예는 monoid입니다. monoid는 τ함수 mappend ∷ (τ, τ) → τ와 아이디를 가진 모든 유형 입니다 mzero ∷ τ. 다른 예에는 그룹 (추가 invert ∷ τ → τ기능을 제외하고는 모노 이드와 유사 함 ), 고리, 격자 등이 포함됩니다.

모든 기능은 작동 τ하지만 다른 특성을 가질 수 있습니다. 로 우리는이를 쓸 수있는 τⁿ → ττⁿ의 튜플에 매핑 n τ. 이런 식으로, 정체성을 빈 터플이있는 τ⁰ → τ곳 으로 생각하는 것이 합리적 입니다. 따라서 대수학의 개념을 실제로 단순화 할 수 있습니다. 이것은 몇 가지 함수가있는 유형일뿐입니다.τ⁰()

대수는 코드에서하는 것처럼 "계산 된"수학의 일반적인 패턴입니다. 사람들은 앞서 언급 한 monoids, groups, lattices 등 흥미로운 것들이 모두 비슷한 패턴을 따른다는 것을 알아 차렸다. 이 작업의 장점은 프로그래밍과 동일합니다. 재사용 가능한 증명을 만들고 특정 종류의 추론을 더 쉽게 만듭니다.

F- 대수

그러나 우리는 팩토링을 완전히하지 않았습니다. 지금까지 많은 기능이 τⁿ → τ있습니다. 우리는 실제로 그것들을 모두 하나의 함수로 결합하는 깔끔한 트릭을 할 수 있습니다. 특히, monoids에서 살펴 보자 : 우리가 가지고 mappend ∷ (τ, τ) → τmempty ∷ () → τ. 합 유형을 사용하여 이러한 함수를 단일 함수로 변환 할 수 있습니다 Either. 다음과 같이 보일 것입니다 :

op  Monoid τ  Either (τ, τ) ()  τ
op (Left (a, b)) = mappend (a, b)
op (Right ())    = mempty

우리는 실제로 결합 반복이 변환을 사용할 수 있습니다 모든τⁿ → τ 를 들어, 하나 하나에 기능을 어떤 대수. (사실, 우리는 기능의 수에 대해이 작업을 수행 할 수 있습니다 a → τ, b → τ대한상의 있도록 모든 a, b,… .)

이것은 우리가 유형으로 대수에 대해 이야기 할 수 있습니다 τA를 하나 의 약간의 혼란에서 기능 Either의 하나에 τ. 모노 아이드의 경우이 혼란은 다음과 같습니다 Either (τ, τ) ().; (여분이있는 그룹에 대한 τ → τ작업을)은 다음과 같습니다 Either (Either (τ, τ) τ) (). 각 구조마다 유형이 다릅니다. 그렇다면 이러한 모든 유형의 공통점은 무엇입니까? 가장 분명한 것은 그것들이 모두 대수의 데이터 타입이라는 단순한 제품이라는 것입니다. 예를 들어, monoid의 경우 monoid τ에서 작동하는 monoid 인수 유형을 만들 수 있습니다 .

data MonoidArgument τ = Mappend τ τ -- here τ τ is the same as (τ, τ)
                      | Mempty      -- here we can just leave the () out

우리는 그룹과 고리, 격자 및 다른 모든 가능한 구조에 대해 같은 일을 할 수 있습니다.

이 모든 유형에서 특별한 점은 무엇입니까? 글쎄, 그들은 모두입니다 Functors! 예 :

instance Functor MonoidArgument where
  fmap f (Mappend τ τ) = Mappend (f τ) (f τ)
  fmap f Mempty        = Mempty

따라서 대수학에 대한 아이디어를 더 일반화 할 수 있습니다. functor 용 τ함수가있는 유형 일뿐 입니다. 실제로 이것을 타입 클래스로 작성할 수 있습니다.f τ → τf

class Functor f  Algebra f τ where
  op  f τ  τ

이것은 functor에 의해 결정되기 때문에 종종 "F- 대수"라고합니다 F. 타입 클래스를 부분적으로 적용 할 수 있다면 다음과 같이 정의 할 수 class Monoid = Algebra MonoidArgument있습니다.

대수

이제 대수가 무엇인지 그리고 그것이 정상적인 대수 구조를 일반화하는 방법을 잘 알고 있기를 바랍니다. F- 대수 란 무엇입니까? 공동은 그것이 대수의 "이중"이라는 것을 암시합니다. 즉, 우리는 대수를 취하고 약간의 화살표를 뒤집습니다. 위의 정의에는 하나의 화살표 만 표시되므로 간단히 뒤집어 보겠습니다.

class Functor f  CoAlgebra f τ where
  coop  τ  f τ

그리고 그게 다야! 이제이 결론은 약간의 플립 팬트처럼 보일 수 있습니다. 그것은 대수학이 무엇인지 알려주지 만, 그것이 어떻게 유용한 지 또는 왜 우리가 관심을 갖는지에 대한 통찰력을 제공하지는 않습니다. 좋은 예나 두 가지를 찾거나 생각해 내면 조금 더 이해하겠습니다. : P.

클래스와 객체

조금 읽은 후에는 클래스와 객체를 표현하기 위해 대수를 사용하는 방법에 대한 좋은 아이디어가 있다고 생각합니다. C클래스에서 가능한 모든 객체의 내부 상태를 포함 하는 유형 이 있습니다. 클래스 자체는 C객체의 메소드와 속성을 지정 하는 대수 입니다.

대수학의 예와 같이 우리가 같은 기능의 무리가있는 경우, a → τ그리고 b → τ어떤를 들어 a, b,…, 우리는 모든 사용하여 하나의 함수로 결합 할 수 있습니다 Either, 합계 유형을. 이중 "개념"유형의 기능에 무리를 결합하는 것 τ → a, τ → b등등. 합산 유형 (제품 유형)의 이중을 사용하여이 작업을 수행 할 수 있습니다. 위의 두 함수 ( fg)가 주어지면 다음 과 같이 하나의 함수 를 만들 수 있습니다.

both  τ  (a, b)
both x = (f x, g x)

이 유형 (a, a)은 간단한 방식으로 functor이므로 F-coalgebra라는 개념에 꼭 맞습니다. 이 특별한 트릭을 통해 여러 가지 함수 또는 OOP의 경우 메소드를 단일 함수 유형으로 패키지 할 수 있습니다 τ → f τ.

우리 유형의 요소 는 객체 C내부 상태를 나타냅니다 . 객체에 읽을 수있는 속성이있는 경우 상태에 따라 달라질 수 있어야합니다. 이 작업을 수행하는 가장 확실한 방법은 기능을로 만드는 것입니다 C. 그래서 만약 우리가 length 속성을 원한다면 object.length우리는 함수를 가질 것 C → Int입니다.

인수를 취하고 상태를 수정할 수있는 메소드를 원합니다. 이를 위해서는 모든 인수를 취하고 새로운을 생성해야합니다 C. 과 좌표 setPosition를 취하는 방법을 상상해 봅시다 : . 다음과 같이 보일 것입니다 : .xyobject.setPosition(1, 2)C → ((Int, Int) → C)

여기서 중요한 패턴은 객체의 "방법"과 "속성"이 객체 자체를 첫 번째 인수로 취하는 것입니다. 이것은 self파이썬 의 매개 변수와 같으며 this다른 많은 언어 의 암시와 같습니다 . coalgebra는 기본적으로 단지 복용의 동작 캡슐화 self매개 변수를 : 첫 번째 무슨이 C에가 C → F C있다.

자 함께합시다. position속성, name속성 및 setPosition함수가 있는 클래스를 상상해 봅시다 .

class C
  private
    x, y  : Int
    _name : String
  public
    name        : String
    position    : (Int, Int)
    setPosition : (Int, Int)  C

이 클래스를 나타내려면 두 부분이 필요합니다. 먼저 객체의 내부 상태를 나타내야합니다. 이 경우에는 두 개의 Inta와 a 만 보유 합니다 String. (이것은 우리 유형 C입니다.) 그런 다음 클래스를 나타내는 대수를 생각해 내야합니다.

data C = Obj { x, y   Int
             , _name  String }

쓸 두 가지 속성이 있습니다. 그들은 아주 사소합니다.

position  C  (Int, Int)
position self = (x self, y self)

name  C  String
name self = _name self

이제 위치를 업데이트 할 수 있어야합니다.

setPosition  C  (Int, Int)  C
setPosition self (newX, newY) = self { x = newX, y = newY }

이것은 명시 적 self변수를 가진 파이썬 클래스와 같습니다 . 이제 우리는 많은 self →기능을 가지고 있으므로 대수를위한 단일 기능으로 결합해야합니다. 간단한 튜플 로이 작업을 수행 할 수 있습니다.

coop  C  ((Int, Int), String, (Int, Int)  C)
coop self = (position self, name self, setPosition self)

타입 ((Int, Int), String, (Int, Int) → c)용 - 어떤이 c 때문에, 펑터를 -is coop우리가 원하는 형태를 가지고있다 : Functor f ⇒ C → f C.

이를 감안할 C와 함께 coop내가 위에서 준 클래스를 지정하는 coalgebra 형태. 이 같은 기술을 사용하여 객체에 대해 원하는 수의 메서드와 속성을 지정하는 방법을 알 수 있습니다.

이것은 우리가 수업을 다루기 위해 대 수학적 추론을 사용할 수있게합니다. 예를 들어, 클래스 간 변환을 나타 내기 위해 "F- 대수 형 동질성"이라는 개념을 도입 할 수 있습니다. 이것은 무서운 소리의 용어로, 구조를 유지하는 대지 사이의 변형을 의미합니다. 따라서 클래스를 다른 클래스에 매핑하는 것이 훨씬 쉬워집니다.

간단히 말해, F- 대수학은 self각 객체의 내부 상태를 포함 하는 매개 변수에 의존하는 많은 속성과 메서드를 사용하여 클래스를 나타냅니다 .

다른 카테고리

지금까지 Haskell 유형의 대수와 대수에 대해 이야기했습니다. 대수학 그냥 유형 τ함수와 f τ → τ와 coalgebra 단지 유형 τ기능과 함께 τ → f τ.

그러나 실제로 이러한 아이디어를 Haskell 자체 와 관련시키는 것은 없습니다 . 실제로, 이들은 일반적으로 유형 및 하스켈 함수보다는 세트 및 수학 함수로 소개됩니다. 실제로 이러한 개념을 모든 범주로 일반화 할 수 있습니다 !

일부 범주에 대해 F- 대수를 정의 할 수 있습니다 C. 먼저 functor, F : C → Cendofunctor 가 필요합니다 . (모든 하스켈 Functor의 사실에서 endofunctors 있습니다 Hask → Hask.) 그런 다음, 대수는 것을 목적으로한다 A에서 Cmorphism에와 F A → A. 대수는를 제외하고 동일 A → F A합니다.

다른 범주를 고려하면 무엇을 얻을 수 있습니까? 글쎄, 우리는 다른 맥락에서 같은 아이디어를 사용할 수 있습니다. 모나드처럼. Haskell에서 모나드는 M ∷ ★ → ★세 가지 연산 이있는 유형 입니다.

map         β)  (M α  M β)
return    α  M α
join      M (M α)  M α

map기능은 사실 단지 증거 M입니다 Functor. 그래서 우리는 모나드가있는 펑터 그냥 말할 수있는 두 가지 작업이 : returnjoin.

펑 터는 범주 자체를 형성하며 그 사이의 형태는 소위 "자연 변환"입니다. 자연스러운 변형은 구조를 유지하면서 하나의 functor를 다른 functor로 변형시키는 방법입니다. 여기 아이디어를 설명 돕는 좋은 기사. 에 대한 이야기는 목록에 concat불과 join합니다.

Haskell functors를 사용하면 두 functors의 구성이 functor 자체입니다. 의사 코드에서 다음과 같이 작성할 수 있습니다.

instance (Functor f, Functor g)  Functor (f  g) where
  fmap fun x = fmap (fmap fun) x

이를 통해 join의 매핑으로 생각할 수 있습니다 f ∘ f → f. 의 유형은 join입니다 ∀α. f (f α) → f α. 직관적으로 모든 유형에 유효한 함수 α가의 변환으로 생각되는 방법을 알 수 있습니다 f.

return비슷한 변형입니다. 유형은 ∀α. α → f α입니다. 이것은 다르게 보인다 – 첫 번째 α는 펑터 (functor)에 있지 않다! 행복하게도 여기에 ID functor를 추가하여이 문제를 해결할 수 있습니다 ∀α. Identity α → f α. return변신도 마찬가지 입니다 Identity → f.

이제 우리는 어떤 펑를 기반으로 그냥 대수로 모나드에 대해 생각할 수있는 f작업으로 f ∘ f → fIdentity → f. 익숙하지 않습니까? 그것은 monoid와 매우 유사하며, 이는 τ연산 τ × τ → τ과 일부 유형 일뿐 () → τ입니다.

따라서 모나드는 유형을 갖는 대신 functor를 갖는 것을 제외하고는 monoid와 같습니다. 다른 종류의 대수와 같은 종류입니다. (여기서 "모나드는 endofunctors 범주에서 단일체 일뿐입니다"라는 문구가 내가 아는 곳에서 유래 한 곳입니다.)

이제, 우리는이 두 가지 작업이 : f ∘ f → fIdentity → f. 해당 대수를 얻으려면 화살표를 뒤집습니다. 이것은 우리에게 두 가지 새로운 작업을 제공합니다 : f → f ∘ ff → Identity. 위와 같이 유형 변수를 추가하여 ∀α. f α → f (f α)와를 제공하여 Haskell 유형으로 바꿀 수 있습니다 ∀α. f α → α. 이것은 comonad의 정의와 같습니다.

class Functor f  Comonad f where
  coreturn  f α  α
  cojoin    f α  f (f α)

그래서 comonad은 그 다음이다 coalgebra endofunctors의 범주이다.


45
이것은 매우 귀중합니다. 나는이 전체 F- 대수 사업에 대한 독서와 예에서 (예를 들어, 대격주의와 함께 사용하는 것을 보는) 직관을 모호했지만, 이것은 나에게도 명백합니다. 감사!
Luis Casillas

28
이것은 훌륭한 설명입니다.
에드워드 KMETT

5
@ EdwardKmett : 감사합니다. 클래스와 객체에 대해 추가 한 것은 괜찮습니까? 나는 오늘 그것에 대해서만 읽었지만 말이되는 것 같습니다.
티콘 젤 비스

7
가치가있는 것 : 여기에서 "endofunctors의 범주"는 더 정확하게는 객체가 어떤 범주에서 endofunctors이고 화살표가 자연스럽게 변형 된 범주입니다. 이것에 대응 펑 조성물하는 monoidal 카테고리이다 (,)과의 신원 펑 (). 단일체 범주 내의 단일체 객체는 단일체 대수에 대응하는 화살표가있는 물체로, 제품 유형이 단일체 구조로 Hask의 단일체를 기술합니다. C의 endofunctor 카테고리에서 monoid 객체는 C의 모나드이므로 이해해야합니다. :]
CA McCann

8
그것은 서사적 인 결말이었습니다!
jdinunzio

85

F- 대수와 F- 대수는 유도 유형 (또는 재귀 유형 ) 을 추론하는 데 도움이되는 수학적 구조입니다 .

F 대수

먼저 F- 대수부터 시작하겠습니다. 최대한 단순하게 노력하겠습니다.

재귀 유형이 무엇인지 알고 있습니다. 예를 들어, 이것은 정수 목록의 유형입니다.

data IntList = Nil | Cons (Int, IntList)

재귀 적이라는 것은 명백합니다. 실제로 그 정의는 자신을 나타냅니다. 정의는 다음과 같은 유형의 두 데이터 생성자로 구성됩니다.

Nil  :: () -> IntList
Cons :: (Int, IntList) -> IntList

필자는 단순히 형식이 아닌 Nilas 형식을 작성했습니다 . 유형은 거주자가 하나뿐 이므로 이론 상으로는 실제로 동등한 유형입니다 .() -> IntListIntList()

이러한 함수의 서명을보다 이론적 인 방식으로 작성하면

Nil  :: 1 -> IntList
Cons :: Int × IntList -> IntList

여기서 1유닛 세트 (하나 개의 원소로 설정)하고 A × B동작이 두 세트의 교차 제품 AB(즉, 쌍들의 세트는 (a, b)여기서 a모든 요소를 통과 A하고b 모든 요소 통과 B).

두 세트의 분리 합집합 AB집합이다 A | B집합의 합집합 {(a, 1) : a in A}{(b, 2) : b in B}. 본질적으로 모두 A와 의 모든 요소 집합 B이지만이 요소들 각각은 A또는에 속하는 것으로 '표시'되어 B있으므로 요소를 선택할 A | B때이 요소의 출처인지 여부를 즉시 알 수 A있습니다 B.

우리는 '결합 (join)' NilCons함수 (function)를 통해 하나의 함수를 하나의 함수로 구성 할 수 있습니다 1 | (Int × IntList).

Nil|Cons :: 1 | (Int × IntList) -> IntList

실제로 Nil|Cons함수가 ()값에 적용되면 (명백하게 1 | (Int × IntList)set에 속함 ) 마치 마치 마치 마치 함수 처럼 작동합니다 Nil. Nil|Cons유형의 값에 적용되는 경우 (Int, IntList)(이러한 값도 세트 1 | (Int × IntList)에 있음)으로 작동합니다 Cons.

이제 다른 데이터 유형을 고려하십시오.

data IntTree = Leaf Int | Branch (IntTree, IntTree)

다음과 같은 생성자가 있습니다.

Leaf   :: Int -> IntTree
Branch :: (IntTree, IntTree) -> IntTree

하나의 함수로 결합 될 수도 있습니다 :

Leaf|Branch :: Int | (IntTree × IntTree) -> IntTree

이 두 joined기능은 비슷한 유형을 가지고 있음을 알 수 있습니다 .

f :: F T -> T

여기서 F우리 형 소요 구성보다 복잡한 형태, 범 변형의 종류 x|작업의 용도 T가능한 기타 유형. 예를 들어, 대한 IntListIntTree F 외모는 다음과 같습니다 :

F1 T = 1 | (Int × T)
F2 T = Int | (T × T)

우리는 모든 대수적 유형이 이런 식으로 쓰여질 수 있음을 즉시 알 수 있습니다. 사실, 이것이 '대수'라고 불리는 이유는 다른 유형의 많은 '합'(unions)과 'products'(교차 제품)로 구성됩니다.

이제 F- 대수를 정의 할 수 있습니다. F-대수는 단지 한 쌍 (T, f), T몇 가지 유형과 f유형의 함수이다 f :: F T -> T. 우리의 예에서 F-대수는 (IntList, Nil|Cons)(IntTree, Leaf|Branch). 그러나 해당 유형의 f기능 에도 불구하고 각 F에 대해 동일 T하며 f자체는 임의적 일 수 있습니다. 예를 들어, (String, g :: 1 | (Int x String) -> String)또는 (Double, h :: Int | (Double, Double) -> Double)일부 gh 해당 F에 대한 F- 대수이기도합니다.

이후에 우리는 F- 대수 동형초기 F- 대수를 도입 할 수 있는데, 이것은 매우 유용한 특성을 가지고 있습니다. 실제로 (IntList, Nil|Cons)초기 F1- 대수이며 (IntTree, Leaf|Branch)초기 F2- 대수입니다. 나는이 용어들과 속성들이 필요보다 복잡하고 추상적이기 때문에 정확한 정의를 제시하지 않을 것이다.

그럼에도 불구하고 (IntList, Nil|Cons)F 대수 라는 사실 은 우리 fold가이 유형에 대해 비슷한 기능 을 정의 할 수있게 합니다. 아시다시피 fold는 일부 재귀 데이터 유형을 하나의 유한 값으로 변환하는 일종의 연산입니다. 예를 들어, 정수 목록을 단일 값으로 접을 수 있는데, 이는 목록의 모든 요소의 합계입니다.

foldr (+) 0 [1, 2, 3, 4] -> 1 + 2 + 3 + 4 = 10

모든 재귀 데이터 유형에 대해 이러한 작업을 일반화 할 수 있습니다.

다음은 foldr기능 의 서명입니다 .

foldr :: ((a -> b -> b), b) -> [a] -> b

중괄호를 사용하여 처음 두 인수를 마지막 인수와 구분했습니다. 이것은 실제 foldr기능은 아니지만 동형입니다 (즉, 쉽게 다른 하나에서 얻을 수 있으며 그 반대도 가능합니다). 부분적으로 적용되는 foldr서명은 다음과 같습니다.

foldr ((+), 0) :: [Int] -> Int

이것이 정수 목록을 가져와 단일 정수를 반환하는 함수임을 알 수 있습니다. 우리의 IntList유형으로 이러한 기능을 정의합시다 .

sumFold :: IntList -> Int
sumFold Nil         = 0
sumFold (Cons x xs) = x + sumFold xs

우리는이 함수는 두 개의 부분으로 구성되어 있음을 볼 수 : 첫 번째 부분에이 기능의 동작을 정의 Nil의 일부 IntList, 그리고 두 번째 부분에 함수의 동작을 정의 Cons부분이다.

이제 우리가 Haskell이 아니라 타입 서명에서 직접 대수 타입을 사용할 수있게하는 언어로 프로그래밍한다고 가정 해 봅시다 Either a b. 기능을 고려하십시오.

reductor :: () | (Int × Int) -> Int
reductor ()     = 0
reductor (x, s) = x + s

F- 대수의 정의 에서처럼 reductortype의 함수 라는 것을 알 수 있습니다 F1 Int -> Int! 실제로, 쌍 (Int, reductor)은 F1 대수입니다.

때문에 IntList초기 F1 대수 각 타입이며 T, 각 함수 r :: F1 T -> T호출하는 기능은 존재, catamorphism 위한 r, 변환 IntListT, 이러한 기능은 독특하다. 실제로,이 예에서에 대한 변이는 reductor입니다 sumFold. 방법 reductorsumFold유사함에 유의하십시오 : 구조는 거의 동일합니다! 으로 reductor정의 s파라미터 사용량 (유형은 어느에 대응하는 T의 연산 결과의 사용에 대응한다)를 sumFold xssumFold정의.

더 명확하게하고 패턴을 볼 수 있도록 돕기 위해 여기 또 다른 예가 있으며 결과 폴딩 기능부터 다시 시작합니다. append첫 번째 인수를 두 번째 인수에 추가하는 함수를 고려하십시오 .

(append [4, 5, 6]) [1, 2, 3] = (foldr (:) [4, 5, 6]) [1, 2, 3] -> [1, 2, 3, 4, 5, 6]

이것이 우리의 모습입니다 IntList:

appendFold :: IntList -> IntList -> IntList
appendFold ys ()          = ys
appendFold ys (Cons x xs) = x : appendFold ys xs

다시, 리 덕터를 작성해 봅시다 :

appendReductor :: IntList -> () | (Int × IntList) -> IntList
appendReductor ys ()      = ys
appendReductor ys (x, rs) = x : rs

appendFold위한 catamorphism이다 appendReductor되는 변형 IntList으로 IntList.

따라서 기본적으로 F- 대수를 사용하면 재귀 데이터 구조, 즉 구조를 일부 값으로 줄이는 작업에 대해 '폴드'를 정의 할 수 있습니다.

F- 대수

F- 대수학은 F- 대수학에 대한 소위 '이중'용어입니다. 이를 통해 unfolds재귀 데이터 유형, 즉 어떤 값에서 재귀 구조를 구성 할 수있는 방법 을 정의 할 수 있습니다.

다음 유형이 있다고 가정하십시오.

data IntStream = Cons (Int, IntStream)

이것은 무한한 정수 스트림입니다. 유일한 생성자는 다음 유형을 갖습니다.

Cons :: (Int, IntStream) -> IntStream

또는 세트면에서

Cons :: Int × IntStream -> IntStream

Haskell을 사용하면 데이터 생성자에서 패턴 일치를 수행 할 수 있으므로 IntStreams에서 작동하는 다음 함수를 정의 할 수 있습니다 .

head :: IntStream -> Int
head (Cons (x, xs)) = x

tail :: IntStream -> IntStream
tail (Cons (x, xs)) = xs

자연스럽게 이러한 함수를 유형의 단일 함수로 '결합'할 수 있습니다 IntStream -> Int × IntStream.

head&tail :: IntStream -> Int × IntStream
head&tail (Cons (x, xs)) = (x, xs)

함수의 결과가 우리 IntStream유형의 대수적 표현과 어떻게 일치하는지 주목하십시오 . 다른 재귀 데이터 형식에 대해서도 비슷한 작업을 수행 할 수 있습니다. 아마도 당신은 이미 그 패턴을 알아 차렸을 것입니다. 나는 유형의 기능 패밀리를 언급하고 있습니다.

g :: T -> F T

여기서 T몇 가지 유형입니다. 이제부터는

F1 T = Int × T

이제 F-coalgebra 는 한 쌍 (T, g)이며, 여기서 T유형 g은 유형의 함수입니다 g :: T -> F T. 예를 들어, (IntStream, head&tail)F1 대수입니다. 다시, F- 대수에서 gT같이 임의적 일 수 있으며, 예를 들어, (String, h :: String -> Int x String)일부 h에 대한 F1- 대수 이기도합니다.

모든 F- 대수학 들 중에서 소위 터미널 F- 대수학이 있는데, 이들은 초기 F- 대수학에 이중이다. 예를 들어, IntStream터미널 F- 대수입니다. 각 타입에 대해,이 수단은 T모든 함수 p :: T -> F1 T호출 된 함수가 존재 anamorphism , 변환 TIntStream, 이러한 기능을 유일하다.

주어진 함수에서 시작하여 연속적인 정수 스트림을 생성하는 다음 함수를 고려하십시오.

nats :: Int -> IntStream
nats n = Cons (n, nats (n+1))

이제이 기능 검사하자 natsBuilder :: Int -> F1 Int,이다 natsBuilder :: Int -> Int × Int:

natsBuilder :: Int -> Int × Int
natsBuilder n = (n, n+1)

다시 말하지만, 우리는 사이에 유사성 볼 수 있습니다 natsnatsBuilder. 이는 리 덕터와 접힘에서 이전에 관찰 한 연결과 매우 유사합니다. nats의 아나 모피 즘입니다 natsBuilder.

또 다른 예는 값과 ​​함수를 가져와 함수의 연속적인 응용 프로그램 스트림을 값으로 반환하는 함수입니다.

iterate :: (Int -> Int) -> Int -> IntStream
iterate f n = Cons (n, iterate f (f n))

빌더 기능은 다음과 같습니다.

iterateBuilder :: (Int -> Int) -> Int -> Int × Int
iterateBuilder f n = (n, f n)

그런 다음 iterate의 아나몰픽입니다 iterateBuilder.

결론

즉, F- 대수는 폴드를 정의 할 수 있습니다. 즉, 재귀 구조를 단일 값으로 낮추는 연산을 허용하고 F- 대수는 반대를 수행 할 수 있습니다. 단일 값에서 [잠재적으로] 무한 구조를 구성합니다.

실제로 Haskell F- 대수와 F- 대수는 일치합니다. 이것은 각 유형에 '하단'값이 존재하기 때문에 매우 좋은 속성입니다. 따라서 Haskell에서는 모든 재귀 유형에 대해 접기 및 접기를 모두 만들 수 있습니다. 그러나 이것의 배후에있는 이론적 모델은 위에서 제시 한 것보다 더 복잡하므로 의도적으로 피했습니다.

도움이 되었기를 바랍니다.


의 유형과 정의 appendReductor는 약간 이상하게 보이며 실제로 패턴을 보는 데 도움이되지 않았습니다 ... :) 올바른지 다시 확인할 수 있습니까? .. 일반적으로 리 덕터 유형은 어떻게 생겼습니까? r거기에 대한 정의 F1에서 IntList에 의해 결정됩니까, 아니면 임의의 F입니까?
Max Galkin

37

자습서 논문 살펴보기 (공) 대수와 (공) 유도에 관한 자습서 는 컴퓨터 과학의 대수에 대한 통찰력을 제공합니다.

아래는 당신을 설득하기 위해 인용 한 내용입니다.

일반적으로 일부 프로그래밍 언어의 프로그램은 데이터를 조작합니다. 지난 수십 년 동안 컴퓨터 과학이 발전하는 동안, 예를 들어 프로그램이 운영되는 데이터의 특정 표현에 의존하지 않도록하기 위해 이러한 데이터에 대한 추상적 인 설명이 바람직하다는 것이 분명해졌습니다. 또한 이러한 추상화는 정확성 증명을 용이하게합니다.
이러한 요구로 인해 컴퓨터 과학, 대수 사양 또는 추상 데이터 유형 이론이라는 지점에서 대수적 방법이 사용되었습니다. 연구 대상은 대수학에 익숙한 기술 개념을 사용하여 데이터 유형 자체입니다. 컴퓨터 과학자가 사용하는 데이터 유형은 종종 주어진 (생성자) 연산 모음에서 생성되며, 이러한 이유로 대수학의 "초기 성"이 중요한 역할을합니다.
표준 대수 기법은 컴퓨터 과학에 사용되는 데이터 구조의 다양한 필수 측면을 캡처하는 데 유용하다는 것이 입증되었습니다. 그러나 컴퓨팅에서 발생하는 본질적으로 역동적 인 구조 중 일부를 대수적으로 설명하는 것은 어려운 것으로 판명되었습니다. 이러한 구조는 일반적으로 여러 가지 방식으로 변형 될 수있는 상태 개념을 포함합니다. 이러한 상태 기반 동적 시스템에 대한 공식적인 접근 방식은 일반적으로 고전적인 초기 기준으로 오토마타 또는 전환 시스템을 사용합니다.
지난 10 년 동안 이러한 국가 기반 시스템이 대수학이 아니라 소위 공대 수학으로 설명되어야한다는 통찰력이 점차 커졌습니다. 이것들은 대수의 공식적인 이중이며,이 튜토리얼에서 정확하게 만들어 질 것입니다. 대수학에 대한 "처음 성"의 이중 속성, 즉 최종성은 그러한 대수학에 결정적인 것으로 밝혀졌습니다. 그리고 그러한 최종 대수학에 필요한 논리적 추론 원칙은 귀납이 아니라 공동 귀납입니다.


범주 이론에 관한 서문. 범주 이론은 functors 이론의 이름을 바꿔야합니다. 범주는 펑터를 정의하기 위해 정의해야하는 범주입니다. 또한 펑 터는 자연 변환을 정의하기 위해 정의해야하는 기능입니다.

펑터 란? 한 세트에서 다른 세트로 변형하여 구조를 보존합니다. (자세한 내용은 인터넷에 대한 좋은 설명이 많이 있습니다).

F- 대수 란 무엇입니까? functor의 대수입니다. 그것은 단지 functor의 보편적 특성에 대한 연구 일뿐입니다.

어떻게 컴퓨터 과학과 연결될 수 있습니까? 프로그램은 구조화 된 정보 세트로 볼 수 있습니다. 프로그램의 실행은이 구조화 된 정보 세트의 수정에 해당합니다. 실행이 프로그램 구조를 보존해야한다는 것이 좋습니다. 그러면 실행은이 정보 세트에 대한 functor의 응용 프로그램으로 볼 수 있습니다. (프로그램을 정의하는 것).

왜 F-co-algebra인가? 프로그램은 정보에 의해 설명되고 그에 따라 행동하므로 본질적으로 이중적입니다. 그러면 주로 프로그램을 구성하고 변경하게하는 정보를 양방향으로 볼 수 있습니다.

  • 프로그램이 처리하는 정보로 정의 할 수있는 데이터.
  • 프로그램이 공유하는 정보로 정의 할 수있는 상태입니다.

그런 다음이 단계에서

  • F- 대수는 데이터의 우주 (여기에 정의 된)에 작용하는 기능적 변형에 대한 연구입니다.
  • F-co-algebras는 (여기에 정의 된) 주 우주에 작용하는 기능적 변형에 대한 연구입니다.

프로그램 수명 동안 데이터와 상태는 공존하며 서로를 완성합니다. 그들은 이중입니다.


5

나는 분명히 프로그래밍과 관련된 것들로 시작한 다음 수학적인 것들을 추가하여 내가 할 수있는 한 구체적이고 완전한 지구로 유지할 것입니다.


공동화에 관한 컴퓨터 과학자 몇 명을 인용 해 보자…

http://www.cs.umd.edu/~micinski/posts/2012-09-04-on-understanding-coinduction.html

유도는 유한 데이터에 관한 것이며, 공동 유도는 무한 데이터에 관한 것입니다.

무한 데이터의 전형적인 예는 지연 목록 (스트림)의 유형입니다. 예를 들어 메모리에 다음과 같은 객체가 있다고 가정 해 봅시다.

 let (pi : int list) = (* some function which computes the digits of
 π. *)

컴퓨터는 제한된 양의 메모리 만 있기 때문에 π를 모두 보유 할 수는 없습니다! 그러나 그것이 할 수있는 일은 유한 한 프로그램을 유지하는 것인데, 원하는 프로그램을 임의로 길게 확장 할 수 있습니다. 유한 한리스트 만 사용하는 한 필요한만큼 무한한리스트로 계산할 수 있습니다.

그러나 다음 프로그램을 고려하십시오.

let print_third_element (k : int list) =   match k with
     | _ :: _ :: thd :: tl -> print thd


 print_third_element pi

이 프로그램은 pi의 세 번째 숫자를 인쇄해야합니다. 그러나 일부 언어에서는 함수에 대한 인수가 함수에 전달되기 전에 평가됩니다 (지연하지 않고 엄격하지 않은 평가). 이 축소 순서를 사용하면 위의 프로그램은 pi의 숫자를 영원히 계산하여 프린터 기능으로 전달되지 않습니다 (절대로 발생하지 않음). 머신에 무한 메모리가 없기 때문에 결국 메모리 부족 및 충돌이 발생합니다. 최상의 평가 순서가 아닐 수도 있습니다.

http://adam.chlipala.net/cpdt/html/Coinductive.html

Haskell과 같은 게으른 기능 프로그래밍 언어에서는 무한한 데이터 구조가 어디에나 있습니다. 무한리스트 및보다 이국적인 데이터 유형은 프로그램의 부분 간 통신에 편리한 추상화를 제공합니다. 무한 게으른 구조없이 유사한 편의를 달성하려면 많은 경우 제어 흐름의 곡예 반전이 필요합니다.

http://www.alexandrasilva.org/#/talks.html Alexandra Silva의 대수의 예


주변 수학 상황을 일반적인 프로그래밍 작업과 관련시키기

"대수"란 무엇입니까?

대수 구조는 일반적으로 다음과 같습니다.

  1. 물건
  2. 물건이 할 수있는 것

이것은 1. 속성과 2. 메소드를 가진 객체처럼 들립니다. 또는 형식 서명처럼 들립니다.

표준 수학적 예에는 모노 이드 ⊃ 그룹 ⊃ 벡터 공간 ⊃ "대수"가 포함됩니다. 모노 이드는 오토마타 : 동사 시퀀스 (예 :)와 같습니다 f.g.h.h.nothing.f.g.f. git항상 결코 역사를 추가하지 않고 로그는이 모노 이드하지만 그룹이 될 것입니다 삭제합니다. 역수를 추가하면 (예 : 음수, 분수, 근, 누적 이력 삭제, 깨진 미러 해제) 그룹이 생깁니다.

그룹에는 함께 더하거나 뺄 수있는 것이 포함됩니다. 예를 들어 Durations를 함께 추가 할 수 있습니다. (단, Dates는 불가능합니다.) 지속 시간은 그룹이 아닌 벡터 공간에 있으며 외부 숫자로도 크기를 조정할 수 있기 때문입니다. (의 형식 서명 scaling :: (Number,Duration) → Duration)

대수 ⊂ 벡터 공간은 또 다른 일을 할 수 있습니다 m :: (T,T) → T. "곱셈"이라고 부르거나하지 마십시오. 일단 Integers"곱셈"(또는 "지수" )이 무엇인지 명확 하지 않기 때문 입니다.

곱셈을해야하는지 그들에게 : 명 (카테고리 이론적) 보편적 인 특성으로 볼 이유 (이다 이렇게처럼 :

제품의 보편적 특성 )


대수 → 대수

공배수는 곱셈보다 임의적이지 않은 방식으로 정의하기가 더 쉽습니다. 왜냐하면 T → (T,T)동일한 요소를 반복해서 사용할 수 있기 때문 입니다. ( "대각선 맵"– 스펙트럼 이론의 대각선 행렬 / 연산자처럼)

코 유닛은 보통 트레이스 (대각선 항목의 합)이지만, 중요한 것은 코 유닛 이하는 일입니다 . trace행렬에 대한 좋은 대답입니다.

이중 공간 을 보는 이유 는 일반적으로 그 공간에서 생각하기가 더 쉽기 때문입니다. 예를 들어, 법선 벡터에 대해 법선 평면보다 생각하기가 더 쉬운 경우가 있지만 벡터로 평면 (하이 평면 포함)을 제어 할 수 있습니다 (이제 광선 추적기와 같이 익숙한 기하학적 벡터에 대해 말하고 있습니다). .


구조화되지 않은 데이터 구조화

수학자들은 TQFT 와 같은 재미있는 것을 모델링 하는 반면 프로그래머는

  • 날짜 / 시간 ( + :: (Date,Duration) → Date),
  • 장소 ( Paris(+48.8567,+2.3508)! 점이 아닌 모양입니다.),
  • 어떤 의미에서 일관된 구조화되지 않은 JSON,
  • 잘못되었지만 가까운 XML
  • 현명한 관계의 부하를 만족시켜야하는 엄청나게 복잡한 GIS 데이터,
  • 정규식 은 당신에게 무언가를 의미 했지만, 펄보다 상당히 적은 것을 의미합니다.
  • 모든 임원의 전화 번호 및 빌라 위치, 그의 (현재 전) 아내와 아이들의 이름, 생일 및 이전의 모든 선물을 담아 야하는 CRM 각각은 믿을 수 없을 정도로 "명백한"관계 (고객에게는 명백한) 관계를 충족해야합니다. 코딩하기 어려운
  • .....

대수학에 대해 이야기 할 때 컴퓨터 과학자들은 대개 직교 곱과 같은 작업을 염두에두고 있습니다. 나는 이것이 "대수학은 하스켈의 대수학"이라고 말할 때 사람들이 의미하는 것이라고 믿는다. 그러나 프로그래머가 Place, Date/Time등의 복잡한 데이터 유형을 모델링하고 가능한 Customer한 실제 모델과 비슷하게 보이도록 (또는 적어도 실제 사용자에 대한 최종 사용자의 견해) 어느 정도 나 유사하게 생각해야합니다. set-world 이상으로 유용 할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.