lambda演算的n元组通常定义为:
1-tuple: λ a t . t a
1-tuple-fst: λ t . t (λ a . a)
2-tuple: λ a b t . t a b
2-tuple-fst: λ t . t (λ a b . a)
2-tuple-snd: λ t . t (λ a b . b)
3-tuple: λ a b c t . t a b c
3-tuple-fst: λ t . t (λ a b c . a)
3-tuple-snd: λ t . t (λ a b c . b)
3-tuple-trd: λ t . t (λ a b c . c)
... and so on.
我的问题是:是否可以实现一个接收教会号码N
并返回任何N的相应N元组的函数?此外,是否可以扩展此功能,以便它还返回相应的访问器? 该算法不能使用任何形式的递归,包括定点组合器。
〜
编辑:根据要求,详细说明我尝试过的内容。
我希望该函数不依赖于递归/定点组合器,因此,显而易见的方法是使用教会数字进行重复。说,我试过随机测试很多表达式,以了解它们是如何成长的。例如:
church_4 (λ a b c . a (b c))
缩减为:
(λ a b c d e f . a ((((e d) c) b) a)))))
我已将许多类似组合church_4 (λ a b c . (a (b c)))
的减少与我期望的结果进行比较,并注意到我可以将访问器实现为:
firstOf = (λ max n . (firstOf (sub max n) (firstOf n)))
access = (λ max idx t . (t (firstOf (sub max idx) (firstOf idx))))
其中sub
是减法运算符,access church_5 church_2
表示访问6元组的第3个元素(因为2是第3个自然元素)。
现在,关于元组。请注意,问题是找到一个术语my_term
,例如:
church_3 my_term
具有以下正常形式:
(λ a b c d t . ((((t a) b) c) d))
正如你所看到的,我几乎找到了它,因为:
church_3 (λ a b c . a (b c)) (λ a . a)
缩减为:
(λ a b c d . (((a b) c) d))
这几乎是我需要的结果,除了缺少t
。
这就是我到目前为止所尝试过的。
答案 0 :(得分:5)
让
foldargs = λ t n f z . (IsZero n) (t z) (λ a . foldargs t (pred n) f (f a z))
然后功能
listofargs = λ n . foldargs id n pair null
返回其args的反转列表:
listofargs 5 a b c d e --> (e . (d . (c . (b . (a . null))))) or [e d c b a]
功能
apply = λ f l . (isnil l) f (apply (f (head l)) (tail l))
将第一个参数(n元函数)应用于从第二个参数(长度为n的列表)中获取的参数:
apply f [a b c d e] --> f a b c d e
其余的很容易:
n-tuple = λ n . foldargs n-tuple' (Succ n) pair null
,其中
n-tuple' = λ l . apply (head l) (reverse (tail l))
其他功能的实施可以从wikipedia获取。
可以通过Y-combinator消除递归。
reverse
很简单。
UPD:函数的非递归版本:
foldargs = Y (λ c t n f z . (IsZero n) (t z) (λ a . c t (pred n) f (f a z)))
apply = Y (λ c f l . (isnil l) f (c (f (head l)) (tail l)))
Y = λ f (λ x . f x x) (λ x . f x x)
 
答案 1 :(得分:5)
让我们尝试实现n-ary元组构造函数。我还将瞄准一个简单的实现,这意味着我尝试坚持消除自然数和元组,并尽量避免使用其他(Church编码)数据结构。
我的策略如下:
这样做的原因是我很快就迷失在无类型的lambda演算中,而且我一定会犯很多错误,而依赖类型的环境让我陷入困境。此外,证明助理对编写任何类型的代码都有很大的帮助。
我使用Agda。我跟type-in-type
作了一些欺骗。这使得Agda不一致,但是对于这个问题,正确类型的Universe将是一个巨大的痛苦,而且我们实际上不太可能在这里遇到不一致。
{-# OPTIONS --type-in-type #-}
open import Data.Nat
open import Data.Vec
我们需要一个n元多态函数的概念。我们将参数类型存储在长度为n
的向量中:
NFun : ∀ {n} → Vec Set n → Set → Set
NFun [] r = r
NFun (x ∷ ts) r = x → NFun ts r
-- for example, NFun (Nat ∷ Nat ∷ []) = λ r → Nat → Nat → r
我们有通常的教会编码元组。 n元元组的构造函数是返回元组的n元函数。
NTup : ∀ {n} → Vec Set n → Set
NTup ts = ∀ {r} → NFun ts r → r
NTupCons : ℕ → Set
NTupCons n = ∀ ts → NFun {n} ts (NTup ts)
我们想要一个类型为∀ {n} → NTupCons n
的函数。我们针对元组构造函数的Vec Set n
参数进行递归。空案例很简单,但缺点是有点棘手:
nTupCons : ∀ {n} → NTupCons n
nTupCons [] x = x
nTupCons (t ∷ ts) x = ?
我们需要NFun ts (NTup (t ∷ ts))
来代替问号。我们知道nTupCons ts
的类型为NFun ts (NTup ts)
,因此我们需要以某种方式从前者获得前者。我们注意到我们需要的只是n-ary函数组合,换句话说就是返回类型为NFun
的函数映射:
compN : ∀ {n A B} (ts : Vec Set n) → (A → B) → NFun ts A → NFun ts B
compN [] f = f
compN (t ∷ ts) f g x = compN ts f (g x)
现在,我们只需要从NTup (t ∷ ts)
获取NTup ts
,因为我们已经在范围内拥有x
类型t
,这非常简单:
nTupCons : ∀ {n} → NTupCons n
nTupCons [] x = x
nTupCons (t ∷ ts) x = compN ts consTup (nTupCons ts)
where
consTup : NTup ts → NTup (t ∷ ts)
consTup tup con = tup (con x)
我们将摆脱Vec Set n
- s并重写函数,以便迭代n
参数。但是,简单迭代不适合nTupCons
,因为这只会为我们提供递归结果(nTupCons ts
),但我们还需要n
的当前compN
索引(因为我们通过迭代compN
来实现n
。所以我们写了一个有点像paramorphism的助手。我们还需要在此处使用Church编码对来通过迭代传递Nat
- s:
zero = λ z s. z
suc = λ n z s. s (n z s)
fst = λ p. p (λ a b. a)
snd = λ p. p (λ a b. b)
-- Simple iteration has type
-- ∀ {A} → A → (A → A) → Nat → A
-- In contrast, we may imagine rec-with-n having the following type
-- ∀ {A} → A → (A → Nat → A) → Nat → A
-- We also pass the Nat index of the hypothesis to the "cons" case
rec-with-n = λ z f n .
fst (
n
(λ p. p z zero)
(λ hyp p. p (f (fst hyp) (snd hyp)) (suc (snd hyp))))
-- Note: I use "hyp" for "hypothesis".
其余的很容易翻译:
compN = λ n. n (λ f. f) (λ hyp f g x. hyp f (g x))
nTupCon =
rec-with-n
(λ x. x)
(λ hyp n. λ x. compN n (λ f g. f (g x)) hyp)
让我们测试它的简单情况:
nTupCon zero =
(λ t. t)
nTupCon (suc zero) =
(λ hyp n. λ x. compN n (λ f g. f (g x)) hyp) (nTupCon zero) zero =
λ x. compN zero (λ f g. f (g x)) (λ t. t) =
λ x. (λ f g. f (g x)) (λ t. t) =
λ x. λ g. (λ t. t) (g x) =
λ x . λ g. g x =
λ x g . g x
nTupCon (suc (suc zero)) =
(λ hyp n. λ x. compN n (λ f g. f (g x)) hyp) (nTupCon (suc zero)) (suc zero) =
λ x. compN (suc zero) (λ f g. f (g x)) (λ a t. t a) =
λ x a. (λ f g. f (g x)) ((λ y t. t y) a) =
λ x a. (λ f g. f (g x)) (λ t. t a) =
λ x a g. (λ t. t a) (g x) =
λ x a g. g x a
似乎有效。
答案 2 :(得分:3)
我找到了!你去了:
nTup = (λ n . (n (λ f t k . (f (λ e . (t (e k))))) (λ x . x) (λ x . x)))
测试:
nTup n1 → (λ (λ (0 1)))
nTup n2 → (λ (λ (λ ((0 1) 2))))
nTup n3 → (λ (λ (λ (λ (((0 1) 2) 3)))))
nTup n4 → (λ (λ (λ (λ (λ ((((0 1) 2) 3) 4))))))
等等。它将元素向后存储,但我不认为我会解决这个问题 - 它看起来更自然。挑战是在最左边的最里面的paren上获得0。正如我所说,我可以很容易地同时获得(0 (1 (2 (3 4))))
和((((4 3) 2) 1) 0)
,但那些不能作为元组工作,因为0
是那里的元素。
谢谢大家!
编辑:我实际上已经解决了这个问题:
nTup = (λ a . (a (λ b c d . (b (λ b . (c b d)))) (λ x . x) (λ x . x)))
保留正确的订单。
nTup n4 → (λ (λ (λ (λ (λ ((((0 4) 3) 2) 1))))))
答案 3 :(得分:2)
如果您可以构建n
- 元组,则可以轻松访问i
索引。
首先,我们需要一个类型用于无限的无类型lambda函数。额外的X
构造函数允许我们通过执行它们来检查这些函数。
import Prelude hiding (succ, pred)
data Value x = X x | F (Value x -> Value x)
instance (Show x) => Show (Value x) where
show (X x) = "X " ++ show x
show _ = "F"
能够相互应用功能很方便。
ap :: Value x -> Value x -> Value x
ap (F f) = f
ap _ = error "Attempt to apply Value"
infixl 1 `ap`
如果您要使用教堂数字对数字进行编码,则需要一些教堂数字。我们还需要减法来计算在索引到n
元组时要跳过多少个附加参数。
idF = F $ \x -> x
zero = F $ \f -> idF
succ = F $ \n -> F $ \f -> F $ \x -> f `ap` (n `ap` f `ap` x)
one = succ `ap` zero
two = succ `ap` one
three = succ `ap` two
four = succ `ap` three
pred = F $ \n -> F $ \f -> F $ \x -> n `ap` (F $ \g -> F $ \h -> h `ap` (g `ap` f)) `ap` (F $ \u -> x) `ap` idF
subtractF = F $ \n -> (n `ap` pred)
常量函数丢弃其第一个参数。如果我们将常数函数迭代一些数字次数,那么会丢掉许多第一个参数。
--drops the first argument
constF = F $ \f -> F $ \x -> f
-- drops i first arguments
constN = F $ \i -> i `ap` constF
我们可以创建另一个常量函数来删除它的第二个参数。如果我们将它重复一些数字次数,它会丢弃许多第二个参数。
-- drops the second argument
constF' = F $ \f -> F $ \a -> F $ \b -> f `ap` a
-- drops n second arguments
constN' = F $ \n -> n `ap` constF'
要索引到n
元组的i
索引(从第一个索引的zero
开始),我们需要删除n-i-1
个参数从一开始就结束并删除i
个参数。
-- drops (n-i-1) last arguments and i first arguments
access = F $ \n -> F $ \i -> constN `ap` i `ap` (constN' `ap` (subtractF `ap` (succ `ap` i) `ap` n) `ap` idF)
我们将定义几个固定大小的示例元组
tuple1 = F $ \a -> F $ \t -> t `ap` a
tuple2 = F $ \a -> F $ \b -> F $ \t -> t `ap` a `ap` b
tuple3 = F $ \a -> F $ \b -> F $ \c -> F $ \t -> t `ap` a `ap` b `ap` c
我们可以用来证明可以生成相应的访问者。
main = do
print $ tuple1 `ap` (X "Example") `ap` (access `ap` one `ap` zero)
print $ tuple2 `ap` (X "Hello") `ap` (X "World") `ap` (access `ap` two `ap` zero)
print $ tuple2 `ap` (X "Hello") `ap` (X "World") `ap` (access `ap` two `ap` one)
print $ tuple3 `ap` (X "Goodbye") `ap` (X "Cruel") `ap` (X "World") `ap` (access `ap` three `ap` zero)
print $ tuple3 `ap` (X "Goodbye") `ap` (X "Cruel") `ap` (X "World") `ap` (access `ap` three `ap` one)
print $ tuple3 `ap` (X "Goodbye") `ap` (X "Cruel") `ap` (X "World") `ap` (access `ap` three `ap` two)
运行此输出
X "Example"
X "Hello"
X "World"
X "Goodbye"
X "Cruel"
X "World"
要构造元组,你需要迭代一些函数,为函数添加参数而不是删除它们。