모노이드(및 반군)

이 게시물에는 Haskell 및 TypeScript( fp-ts )로 작성된 예제가 포함되어 있습니다.

Semigroupsmonoids은 여러 요소를 하나로 축소하는 매우 일반적인 프로그래밍 작업을 캡처하는 수학적 구조입니다. 이와 같은 형식을 통해 우리는 다른 방법으로는 얻을 수 없는 추상화를 만들고 활용할 수 있으며 공통 언어로 다른 개발자에게 우리의 의도를 알릴 수 있습니다.

모노이드를 이해하려면 먼저 반군으로 시작해야 합니다.

연쇄



Semigroup은 동일한 유형의 두 항목을 함께 연결하는 방법을 공식적으로 정의합니다. 예를 들어 배열은 세미그룹입니다.

[1, 2] <> [3, 4] -- [1, 2, 3, 4]



[1, 2].concat([3, 4]) // [1, 2, 3, 4]


더하기 및 곱하기 아래의 숫자 또는 결합 및 분리 아래의 부울에 대해 반그룹 인스턴스를 동등하게 정의할 수 있습니다. 기본 수학에 관심이 있다면 Wikipedia 에서 semigroups에 대해 자세히 읽을 수 있습니다.

더 흥미로운 것은 우리가 정의한 임의의 유형에 대한 세미그룹을 정의하는 기능입니다. Cocktail 유형이 있고 그 중 두 가지를 함께 결합할 수 있기를 원한다고 가정해 보겠습니다. 다음과 같이 유형에 대한 정의가 제공됩니다.

data Cocktail = Cocktail
    { name :: String
    , ingredients :: [String]
    }



type Cocktail = {
    name: string;
    ingredients: string[];
};


그런 다음 모든 칵테일 쌍을 함께 결합할 수 있는 형식의 semigroup 인스턴스를 정의할 수 있습니다.

instance Semigroup Cocktail where
    a <> b = Cocktail (name a <> " " <> name b) (ingredients a <> ingredients b)

mojito = Cocktail "Mojito" ["rum", "mint"]
robroy = Cocktail "Rob Roy" ["scotch", "bitters"]

combined = mojito <> robroy
-- Cocktail { name = "Mojito Rob Roy", ingredients = ["rum", "mint", "scotch", "bitters"] }



const semigroupCocktail: Semigroup<Cocktail> = {
    concat: (a, b) => ({
        name: a.name + ' ' + b.name,
        ingredients: a.ingredients.concat(b.ingredients),
    }),
};

const mojito: Cocktail = { name: 'Mojito', ingredients: ['rum', 'mint'] };
const robroy: Cocktail = { name: 'Rob Roy', ingredients: ['scotch', 'bitters'] };

// { name: 'Mojito Rob Roy', ingredients: ['rum', 'mint', 'scotch', 'bitters'] }
const combined = semigroupCocktail.concat(mojito, robroy);


그리고 우리의 유형을 함께 결합할 수 있습니다! 물론, 우리는 semigroup 형식 없이 이 두 가지 특정 유형을 함께 연결하는 함수를 정의할 수 있지만 이 수학 용어에 익숙한 개발자에게는 한눈에 덜 명확하고 일반적인 추상화에서 재사용할 가능성이 적습니다. 당신은 오리 타이핑에 의존해야 하고 어떤 법률의 준수에 의존할 수 없었습니다. 말하자면, semigroup 유형 클래스를 사용하는 것은 소비자가 유용한 가정을 할 수 있도록 하는 수학적 및 기능적 추상화에서 일반적인 것인 associativity의 법칙을 준수하는 것을 의미합니다.

재미있는 사실: (시도한) semigroup 인스턴스가 연관되지 않은 경우 실제로는 a magma 입니다! 유형 클래스 계층 구조에 존재하지만 연관되지 않고 반그룹도 아닌 마그마를 정의하는 것은 예외적으로 드뭅니다.

신원



우리는 이제 물건을 결합하는 방법을 알고 있지만 아무것도 없을 경우 어떻게 될까요? 이러한 상황에서 우리는 identity element 가 있는 단순히 반군인 모노이드를 정의할 수 있습니다.

ID 요소는 해당 유형의 다른 요소와 결합하여 해당 유형에 대한 변경 사항이 없는 유형의 값입니다. 예를 들어 문자열의 ID는 '' , 덧셈 아래 숫자0 , 곱셈 아래 숫자1 , 합 아래 부울true 등입니다.

배열의 식별 요소는 빈 배열입니다. 이 식별 요소를 주어진 배열에 적용할 때 항상 해당 배열을 변경하지 않고 다시 가져오는 방법을 확인하십시오.

[1, 2] <> [] -- [1, 2]
[] <> [1, 2] -- [1, 2]



[1, 2].concat([]) // [1, 2]
[].concat([1, 2]) // [1, 2]


이전의 Cocktail 유형은 어떻습니까? 두 필드가 각각 이미 모노이드임을 감안할 때 이것은 매우 간단합니다.

instance Monoid Cocktail where
    mempty = Cocktail mempty mempty



const monoidCocktail: Monoid<Cocktail> = getStructMonoid({
    name: monoidString,
    ingredients: A.getMonoid<string>(),
});


그리고 이와 같이 임의의 수의 칵테일을 결합할 수 있습니다.

mconcat []               -- Cocktail { name = "", ingredients = [] }
mconcat [mojito]         -- Cocktail { name = "Mojito", ingredients = ["rum", "mint"] }
mconcat [mojito, robroy] -- Cocktail { name = "Mojito Rob Roy", ingredients = ["rum", "mint", "scotch", "bitters"] }



fold(monoidCocktail)([])               // { name: '', ingredients: [] }
fold(monoidCocktail)([mojito])         // { name: 'Mojito', ingredients: ['rum', 'mint'] }
fold(monoidCocktail)([mojito, robroy]) // { name: 'Mojito Rob Roy', ingredients: ['rum', 'mint', 'scotch', 'bitters'] }


이것은 semigroup 연결 연산을 함수로 사용하고 monoidal identity 요소를 시작 값으로 사용하여 항목 배열을 줄이는 것과 같습니다.


이 게시물은 내 개인 블로그에서도 찾을 수 있습니다.
https://www.samhh.com/blog/monoids-semigroups

좋은 웹페이지 즐겨찾기