Лямбда-исчисление — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Новая страница: «{{В разработке}} \documentclass[11pt,a4paper,oneside]{article} \usepackage[english,russian]{babel} \usepackage[T2A]{fontenc} \usepackage[utf8]{inpute...»)
 
Строка 1: Строка 1:
 
{{В разработке}}
 
{{В разработке}}
  
\documentclass[11pt,a4paper,oneside]{article}
+
== Лямбда-исчисление==
  
\usepackage[english,russian]{babel}
+
''Лямбда-исчисление'' {{---}} формальная система, придуманная в 1930-х годах  
\usepackage[T2A]{fontenc}
 
\usepackage[utf8]{inputenc}
 
\usepackage{graphicx}
 
\usepackage{expdlist}
 
\usepackage{mfpic}
 
\usepackage{comment}
 
\usepackage{listings}
 
\usepackage{epigraph}
 
\usepackage{url}
 
\usepackage{array}
 
%include polycode.fmt
 
%options ghci
 
%format Var v = v
 
%format App x y = x "\times" y
 
%format `App` = " \cdot "
 
%format Lam x = "\lambda" x "\to"
 
%format if' = if
 
%format then' = then
 
%format else' = else
 
%format $ = " "
 
 
 
\long\def\ignore#1{}
 
 
 
\begin{document}
 
 
 
\ignore{
 
\begin{code}
 
import Prelude hiding (id, succ, pred, and, or, catch, head, tail)
 
import qualified Prelude as P
 
\end{code}
 
}
 
 
 
\ignore{
 
\begin{code}
 
import Control.Exception
 
import Control.Monad.State
 
import qualified Data.Map as M
 
import Data.Maybe (fromMaybe)
 
 
 
type Variable = String
 
data Term = Var Variable | Lam Variable Term | App Term Term deriving (Ord)
 
 
 
instance Show Term where
 
    show (Var x) = x
 
    show (Lam x e) = "\\" ++ x ++ "." ++ show e
 
    show (App e1@(Lam _ _) e2) = "(" ++ show e1 ++ ") (" ++ show e2 ++ ")"
 
    show (App e1 (Var v)) = show e1 ++ " " ++ v
 
    show (App e1 e2) = show e1 ++ " (" ++ show e2 ++ ")"
 
 
 
free (Var v)    = [ v ]
 
free (Lam v t)  = filter (/= v) . free $ t
 
free (App t t') = (free t) ++ (free t')
 
 
 
subst :: Term -> Variable -> Term -> Term
 
subst t@(Var v)  var what = if v == var then what else t
 
subst t@(Lam v b) var what = if v == var then t else Lam v (subst b var what)
 
subst (App t t')  var what = App (subst t var what) (subst t' var what)
 
 
 
newname :: [Variable] -> Variable -> Variable
 
newname fv = P.head . filter (not . flip elem fv) . iterate ('_':)
 
 
 
deny:: [String] -> Term -> Term
 
deny d (Var v) = Var v -- Var $ newname d v
 
deny d (Lam v t) = Lam v' $ subst t v (Var v') where v' = newname (d ++ free t) v
 
deny d (App t1 t2) = App (deny d t1) (deny d t2)
 
 
 
unifyBound :: Term -> Term
 
unifyBound e = fst $ runState (unifyBound' e) (M.empty, free e)
 
 
 
unifyBound' :: Term -> State (M.Map Variable Variable, [Variable]) Term
 
unifyBound' (Var v) = do
 
    (m, used) <- get
 
    return $ Var $ fromMaybe v (M.lookup v m)
 
unifyBound' e@(App t1 t2) = do
 
    t1' <- unifyBound' t1
 
    t2' <- unifyBound' t2
 
    return $ App t1' t2'
 
unifyBound' (Lam x t) = do
 
    (m, used) <- get
 
    let new = fst $ runState unused used
 
    put (M.insert x new m, new:used)
 
    e <- unifyBound' t
 
    (_, used') <- get
 
    put (m, used')
 
    return $ Lam new e
 
 
 
eq :: Term -> Term -> Bool
 
eq (Var v) (Var v') = v == v'
 
eq (Lam v1 e1) (Lam v2 e2) = v1 == v2 && e1 `eq` e2
 
eq (App e1 e2) (App e1' e2') = e1 `eq` e1' && e2 `eq` e2'
 
eq _ _ = False
 
 
 
instance Eq Term where
 
    e1 == e2 = free e1 == free e2 && newE1 `eq` newE2 where
 
        newE1 = unifyBound e1
 
        newE2 = unifyBound e2
 
        fv = free e1
 
 
 
unused :: State [Variable] Variable
 
unused = do
 
    used <- get
 
    let new = P.head $ filter (not . (`elem` used)) $ map show [1..]
 
    put $ new:used
 
    return new
 
 
 
no :: Integer -> Term -> Term
 
 
 
no n t = no' n ({- unifyBound -} t)
 
 
 
no' 0 t = error $ "Too long sequence at [" ++ show t ++ "]"
 
no' n t
 
    | reduced = no (n - 1) rest
 
    | otherwise = t
 
    where (reduced, rest) = noOne t
 
 
 
noOne :: Term -> (Bool, Term)
 
noOne (App (Lam v t1) t2) = (True, subst (deny (free t2) t1) v t2)
 
noOne t@(Var v) = (False, t)
 
noOne (Lam v t) = (reduced, Lam v rest)
 
    where (reduced, rest) = noOne t
 
noOne t@(App t1 t2) = if reducedLeft
 
                        then (True, App t1' t2)
 
                        else if reducedRight
 
                            then (True, App t1 t2')
 
                            else (False, t)
 
    where
 
        (reducedLeft, t1') = noOne t1
 
        (reducedRight, t2') = noOne t2
 
        fv = free t2
 
 
 
sa n t = sa' n $ {- unifyBound -} t
 
 
 
sa' 0 t = error $ "Too long sequence at [" ++ show t ++ "]"
 
sa' n t
 
    | reduced = sa (n - 1) rest
 
    | otherwise = t
 
    where (reduced, rest) = saOne t
 
 
 
saOne :: Term -> (Bool, Term)
 
saOne t@(Var v) = (False, t)
 
saOne (Lam v t) = (reduced, Lam v rest)
 
    where (reduced, rest) = saOne t
 
saOne t@(App t1 t2) = if reducedRight
 
        then (True, App t1 t2')
 
        else case t1 of
 
            (Lam v t1'') -> (True, subst (deny (free t2) t1'') v t2) where
 
                (_, t1''') = saOne t1''
 
            _ -> (reducedLeft, App t1' t2)
 
    where
 
        (reducedRight, t2') = saOne t2
 
        (reducedLeft, t1') = saOne t1
 
 
 
normIO :: Int -> Term -> IO Term
 
normIO step t = do
 
    let (reduced, rest) = noOne t
 
    print $ "================" ++ " step " ++ show step ++ " ======== "
 
    if reduced
 
        then normIO (step + 1) rest
 
        else do
 
            print rest
 
            return rest
 
 
 
norm :: Term -> Term
 
norm = no 1000
 
 
 
toInt :: Term -> Int
 
toInt (Var v) = 0
 
toInt (Lam v x) = toInt x
 
toInt (App v1 v2) = (toInt v1) + (toInt v2) + 1
 
 
 
\end{code}
 
}
 
 
 
 
 
\section{Лямбда-исчисление}
 
 
 
\emph{Лямбда-исчисление}~--- формальная система, придуманная в 1930-х годах  
 
 
Алонзо Чёрчем. Лямбда-функция является, по сути, анонимной функцией.
 
Алонзо Чёрчем. Лямбда-функция является, по сути, анонимной функцией.
 
Эта концепция показала себя удобной и сейчас активно используется во многих
 
Эта концепция показала себя удобной и сейчас активно используется во многих
 
языках программирования.
 
языках программирования.
  
Более формально, \emph{лямбда-функцию} (или, \emph{лямбда-терм}) можно задать  
+
Более формально, ''лямбда-функцию'' (или, ''лямбда-терм'') можно задать  
 
следующей грамматикой:
 
следующей грамматикой:
 
+
{{Определение
$$
+
|definition=
 +
<tex>
 
\begin{array}{r c l}
 
\begin{array}{r c l}
 
\langle Term \rangle & ::= & \langle Variable \rangle \\
 
\langle Term \rangle & ::= & \langle Variable \rangle \\
Строка 196: Строка 20:
 
\langle Variable \rangle & ::= & \langle Char \rangle *\\
 
\langle Variable \rangle & ::= & \langle Char \rangle *\\
 
\end{array}
 
\end{array}
$$
+
</tex>
 +
}}
  
 
В первом случае функция является просто переменной.  
 
В первом случае функция является просто переменной.  
Во втором происходит \emph{аппликация} (\emph{применение}) одной функции к другой.
+
Во втором происходит ''аппликация'' (''применение'') одной функции к другой.
 
Это аналогично вычислению функции-левого операнда на аргументе-правом операнде.
 
Это аналогично вычислению функции-левого операнда на аргументе-правом операнде.
В третьем~--- \emph{абстракция} по переменной. В данном случае происходит  
+
В третьем {{---}} ''абстракция'' по переменной. В данном случае происходит  
 
создание функции одного аргумента с заданными именем аргумента и телом функции.
 
создание функции одного аргумента с заданными именем аргумента и телом функции.
  
Рассмотрим, например, функцию |id = \x -> x|. Эта функция принимает аргумент и  
+
Рассмотрим, например, функцию <tex>id = \lambda x \to x</tex>. Эта функция принимает аргумент и  
 
возвращает его неизменённым. Например,  
 
возвращает его неизменённым. Например,  
|id 2 == | \eval{id 2}. Аналогично, |id y == y|.  
+
<tex>id 2 \equiv 2</tex>. Аналогично, <tex>id y == y</tex>.  
  
Ещё один пример функции: |sum = \x -> \y -> x + y|. Эта функция двух аргументов,
+
Ещё один пример функции: <tex>sum = \lambda x \to \lambda y \to x + y|. Эта функция двух аргументов,
 
которая возвращает их сумму. Правда, здесь мы немного вышли за написанную выше грамматику.
 
которая возвращает их сумму. Правда, здесь мы немного вышли за написанную выше грамматику.
Ну да ладно. |sum 2 3 == 5|
+
Ну да ладно. <tex>sum 2 3 == 5</tex>
  
\subsection{Приоритет операций}
+
===Приоритет операций===
\begin{itemize}
+
* Применение левоассоциативно: <tex>x y z w == ((x y) z) w</tex>
\item Применение левоассоциативно: |x y z w == ((x y) z) w|
+
* Аппликация забирает себе всё, до чего дотянется: <tex>\lambda x \to \lambda y \to \lambda z \to z y x \equiv \lambda x \to (\lambda y \to (\lambda z \to ((z y) x)))</tex>
\item Аппликация забирает себе всё, до чего дотянется:\\  
+
* Скобки играют привычную роль группировки действий
    |\x -> \y -> \z -> z y x == \x -> (\y -> (\z -> ((z y) x)))|
 
\item Скобки играют привычную роль группировки действий
 
\end{itemize}
 
  
\subsection{Свободные и связанные переменные}
+
===Свободные и связанные переменные===
\emph{Связанными} переменными называются все переменные, по которым выше в  
+
''Связанными'' переменными называются все переменные, по которым выше в  
 
дереве разбора были абстракции. Все остальные переменные называются свободными.
 
дереве разбора были абстракции. Все остальные переменные называются свободными.
  
Например, в |\x -> \y -> x|, |x| связана, а |y|~--- свободна. А в |\y -> x (\x -> x)|
+
Например, в <tex>\lambda x \to \lambda y \to x</tex>, <tex>x</tex> связана, а <tex>y</tex>{{---}} свободна. А в <tex>\lambda y \to x (\lambda x \to x)</tex>
в своём первом вхождении переменная |x| свободна, а во втором~--- связана.
+
в своём первом вхождении переменная <tex>x</tex> свободна, а во втором {{---}} связана.
  
Рассмотрим функции |\y -> y| и |\x -> y|. В первой из них при взгляде на |y|
+
Рассмотрим функции <tex>\lambda y \to y</tex> и <tex>\lambda x \to y</tex>. В первой из них при взгляде на <tex>y</tex>
 
понятно, что она имеет отношение к переменной, по которой производилась  
 
понятно, что она имеет отношение к переменной, по которой производилась  
 
абстракция. Если по одной и той же
 
абстракция. Если по одной и той же
 
переменной абстракция производилась более одного раза, то переменная связана
 
переменной абстракция производилась более одного раза, то переменная связана
 
с самым поздним (самым нижним в дереве разбора) абстрагированием. Например, в
 
с самым поздним (самым нижним в дереве разбора) абстрагированием. Например, в
|\x -> \x -> \y -> \x -> x|, переменная |x| связана с самой правой абстракцией  
+
<tex>\lambda x \to \lambda x \to \lambda y \to \lambda x \to x</tex>, переменная <tex>x</tex> связана с самой правой абстракцией  
по |x|.
+
по <tex>x</tex>.
  
\subsection{$\alpha$-конверсия}
+
===<tex>\alpha</tex>-конверсия===
  
Рассмотрим функции |(\x -> x) z| и |(\y -> y) z|. Интуитивно понятно, что они  
+
Рассмотрим функции <tex>(\lambda x \to x) z</tex> и <tex>(\lambda y \to y) z</tex>. Интуитивно понятно, что они  
 
являются одинаковыми.  
 
являются одинаковыми.  
  
\emph{$\alpha$-конверсия}~--- переименование связанной переменной. Выражение
+
{{Определение
|\x -> f| можно заменить на |\y -> f[x := y]|, если |y| не входит свободно в |f|,
+
|definition=''<tex>\alpha</tex>-конверсия'' {{---}} переименование связанной переменной. Выражение
где $f[x:=y]$ означает замену всех свободных вхождений $x$ в $f$ на $y$.
+
<tex>\lambda x \to f</tex> можно заменить на <tex>\lambda y \to f[x := y]</tex>, если <tex>y</tex> не входит свободно в <tex>f</tex>,
 +
где <tex>f[x:=y]</tex> означает замену всех свободных вхождений <tex>x</tex> в <tex>f</tex> на <tex>y</tex>.
  
Функции, получающиеся одна из другой с помощью $\alpha$-конверсий, называются  
+
Функции, получающиеся одна из другой с помощью <tex>\alpha</tex>-конверсий, называются  
\emph{$\alpha$-эквивалентными} и обозначаются $f \equiv_\alpha g$.
+
''<tex>\alpha</tex>-эквивалентными'' и обозначаются <tex>f \equiv_\alpha g</tex>.
  
Функции |\x -> \y -> x y z| и |\a -> \x -> a x z| являются $\alpha$-эквивалентными,
+
Функции <tex>\lambda x \to \lambda y \to x y z</tex> и <tex>\lambda a \to \lambda x \to a x z</tex> являются <tex>\alpha</tex>-эквивалентными,
а |\x -> \y -> y z| и |\y -> \x -> y z|~--- нет.
+
а <tex>\lambda x \to \lambda y \to y z</tex> и <tex>\lambda y \to \lambda x \to y z</tex> {{---}} нет.
  
 
\subsection{$\beta$-редукция}
 
\subsection{$\beta$-редукция}

Версия 14:29, 6 декабря 2012

Эта статья находится в разработке!

Лямбда-исчисление

Лямбда-исчисление — формальная система, придуманная в 1930-х годах Алонзо Чёрчем. Лямбда-функция является, по сути, анонимной функцией. Эта концепция показала себя удобной и сейчас активно используется во многих языках программирования.

Более формально, лямбда-функцию (или, лямбда-терм) можно задать следующей грамматикой:

Определение:
[math] \begin{array}{r c l} \langle Term \rangle & ::= & \langle Variable \rangle \\ & || & \langle Term \rangle \langle Term \rangle \\ & || & \lambda \langle Variable \rangle \to \langle Term \rangle\\ & || & ( \langle Term \rangle )\\ \langle Variable \rangle & ::= & \langle Char \rangle *\\ \end{array} [/math]


В первом случае функция является просто переменной. Во втором происходит аппликация (применение) одной функции к другой. Это аналогично вычислению функции-левого операнда на аргументе-правом операнде. В третьем — абстракция по переменной. В данном случае происходит создание функции одного аргумента с заданными именем аргумента и телом функции.

Рассмотрим, например, функцию [math]id = \lambda x \to x[/math]. Эта функция принимает аргумент и возвращает его неизменённым. Например, [math]id 2 \equiv 2[/math]. Аналогично, [math]id y == y[/math].

Ещё один пример функции: [math]sum = \lambda x \to \lambda y \to x + y|. Эта функция двух аргументов, которая возвращает их сумму. Правда, здесь мы немного вышли за написанную выше грамматику. Ну да ладно. \lt tex\gt sum 2 3 == 5[/math]

Приоритет операций

  • Применение левоассоциативно: [math]x y z w == ((x y) z) w[/math]
  • Аппликация забирает себе всё, до чего дотянется: [math]\lambda x \to \lambda y \to \lambda z \to z y x \equiv \lambda x \to (\lambda y \to (\lambda z \to ((z y) x)))[/math]
  • Скобки играют привычную роль группировки действий

Свободные и связанные переменные

Связанными переменными называются все переменные, по которым выше в дереве разбора были абстракции. Все остальные переменные называются свободными.

Например, в [math]\lambda x \to \lambda y \to x[/math], [math]x[/math] связана, а [math]y[/math]— свободна. А в [math]\lambda y \to x (\lambda x \to x)[/math] в своём первом вхождении переменная [math]x[/math] свободна, а во втором — связана.

Рассмотрим функции [math]\lambda y \to y[/math] и [math]\lambda x \to y[/math]. В первой из них при взгляде на [math]y[/math] понятно, что она имеет отношение к переменной, по которой производилась абстракция. Если по одной и той же переменной абстракция производилась более одного раза, то переменная связана с самым поздним (самым нижним в дереве разбора) абстрагированием. Например, в [math]\lambda x \to \lambda x \to \lambda y \to \lambda x \to x[/math], переменная [math]x[/math] связана с самой правой абстракцией по [math]x[/math].

[math]\alpha[/math]-конверсия

Рассмотрим функции [math](\lambda x \to x) z[/math] и [math](\lambda y \to y) z[/math]. Интуитивно понятно, что они являются одинаковыми.


Определение:
[math]\alpha[/math]-конверсия — переименование связанной переменной. Выражение

[math]\lambda x \to f[/math] можно заменить на [math]\lambda y \to f[x := y][/math], если [math]y[/math] не входит свободно в [math]f[/math], где [math]f[x:=y][/math] означает замену всех свободных вхождений [math]x[/math] в [math]f[/math] на [math]y[/math].

Функции, получающиеся одна из другой с помощью [math]\alpha[/math]-конверсий, называются [math]\alpha[/math]-эквивалентными и обозначаются [math]f \equiv_\alpha g[/math].

Функции [math]\lambda x \to \lambda y \to x y z[/math] и [math]\lambda a \to \lambda x \to a x z[/math] являются [math]\alpha[/math]-эквивалентными, а [math]\lambda x \to \lambda y \to y z[/math] и [math]\lambda y \to \lambda x \to y z[/math] — нет.

\subsection{$\beta$-редукция}

$\beta$-редукция олицетворяет идею счёта значения функции. Выражение вида
\rangle$. При этом, голова списка будет храниться как показатель степени при $p_{len}$.

\begin{code} empty = pair' `App` zero' `App` one' cons = Lam "h" $ Lam "t" $ pair' `App` (succ' `App` (fst `App` Var "t"))

       `App` (mult' `App` (snd `App` Var "t") `App` (power' 
               `App` (ithPrime `App` (fst `App` Var "t"))
               `App` Var "h"
       ))

head = Lam "list" $ getExponent `App` (snd `App` Var "list")

                               `App` (ithPrime `App` (pred' `App` (fst `App` Var "list")))

tail = Lam "list" $ pair' `App` (pred' `App` (fst `App` Var "list"))

                         `App` (eliminateMultiplier `App` (snd `App` Var "list")
                                 `App` (ithPrime `App` (pred' `App` (fst `App` Var "list" )))
                               )

eliminateMultiplier' = Lam "f" $ Lam "n" $ Lam "m" $ if `App` (isZero' `App` (mod' `App` Var "n" `App` Var "m"))

                       `App` (Var "f" `App` (div' `App` Var "n" `App` Var "m") `App` Var "m")
                       `App` Var "n"

eliminateMultiplier = fix' `App` eliminateMultiplier'

getExponent' = Lam "f" $ Lam "n" $ Lam "m" $ if `App` (isZero' `App` (mod' `App` Var "n" `App` Var "m"))

               `App` (succ' `App`(Var "f" `App` (div' `App` Var "n" `App` Var "m") `App` Var "m"))
               `App` zero'

getExponent = fix' `App` getExponent' \end{code}

На основе этого всего уже можно реализовать эмулятор машины тьюринга: с помощью пар, списков чисел можно хранить состояния. С помощью рекурсии можно обрабатывать переходы. Входная строка будет даваться, например, закодированной аналогично списку: пара из длины и числа, характеризующего список степенями простых. Я бы продолжил это писать, но уже на операции $head [1, 2]$ я не дождался окончания выполнения. Скорость лямбда-исчисления как вычислителя печальна.

\ignore{ \begin{code}

four' = norm $ succ' `App` three' five' = norm $ succ' `App` four'

list2 = norm $ cons `App` one' `App` empty list32 = cons `App` zero' `App` list2

normFiveFact = normIO 0 $ fact' `App` five'

-- fiftysix = mult twentyeight two -- fiftyfive = pred fiftysix -- six = pred seven

--main = do print $ fiftysix (+1) 0 main = do

--   f <- normIO 0 $ ithPrime `App` three'
--   f <- normIO 0 $ getExponent `App` (norm $ plus' `App` four' `App` four') `App` two'
   f <- normIO 0 $ head `App` (tail `App` list32)
   print $ toInt f

\end{code} }

\end{document} [1]