- 任何人都可以举出功能组合的例子吗?
这是函数组合算子的定义?
(.) :: (b -> c) -> (a -> b) -> a -> c f . g = \x -> f (g x)
这表明它需要两个函数并返回一个函数,但我记得有人用英语表达了这样的逻辑
男孩是人 -> 阿里是男孩 -> 阿里是人
- 这个逻辑与函数组合有什么关系?
- 函数应用和组合的强绑定是什么意思,哪一个比另一个绑定更强?
请帮忙。
谢谢。
这是函数组合算子的定义?
(.) :: (b -> c) -> (a -> b) -> a -> c
f . g = \x -> f (g x)
这表明它需要两个函数并返回一个函数,但我记得有人用英语表达了这样的逻辑
男孩是人 -> 阿里是男孩 -> 阿里是人
请帮忙。
谢谢。
(编辑1:我第一次错过了您问题的几个组成部分;请参阅我的答案的底部。)
考虑这种陈述的方法是查看类型。您拥有的论证形式称为三段论;但是,我认为您记错了一些东西。有许多不同种类的三段论,据我所知,你的三段论并不对应于功能组合。让我们考虑一种三段论:
这称为假设三段论。在逻辑上,我们可以这样写:S代表命题“天气晴朗”,H代表命题“我会变热”,W代表命题“我要去游泳” . 将“ α蕴含β ”写成α → β,将“因此”写成∴,我们可以将上面的内容翻译成:
当然,如果我们将S、H和W替换为任意的α、β和γ ,这将有效。现在,这应该看起来很熟悉。如果我们将蕴含箭头 → 更改为函数箭头->
,则变为
a -> b
b -> c
a -> c
瞧,我们有组合运算符类型的三个组件!要将其视为逻辑三段论,您可以考虑以下内容:
a
,我可以产生一个 type 的值b
。b
,我可以产生一个 type 的值c
。a
,我可以产生一个 type 的值c
。这应该是有道理的:在 中f . g
,函数的存在g :: a -> b
告诉您前提 1 为真,并f :: b -> c
告诉您前提 2 为真。因此,您可以得出最终陈述,该陈述是该函数f . g :: a -> c
的见证。
我不完全确定你的三段论翻译成什么。这几乎是一个例子modus ponens,但不完全是。Modus ponens 论证采用以下形式:
写R代表“下雨”,写W代表“我会淋湿”,这给了我们逻辑形式
用函数箭头替换蕴含箭头会给我们以下结果:
a -> b
a
b
而这只是简单的函数应用,我们可以从($) :: (a -> b) -> a -> b
. 如果您想将此视为一个逻辑论证,它可能是以下形式
a
,我可以产生一个 type 的值b
。a
。b
。在这里,考虑表达式f x
。函数f :: a -> b
是命题1真实性的见证;该值x :: a
是命题 2 真实性的见证;因此结果可以是 type b
,证明结论。这正是我们从证明中发现的。
现在,您的原始三段论采用以下形式:
让我们把它翻译成符号。 Bx将表示x是一个男孩;Hx将表示x是人类;a will 表示阿里;和∀ x。φ表示φ对于x的所有值都为真。然后我们有
这几乎是 ponens 的方式,但它需要实例化 forall。虽然在逻辑上是有效的,但我不确定如何在类型系统级别解释它;如果有人想帮忙,我会全力以赴。一种猜测是 rank-2 类型(forall x. B x -> H x) -> B a -> H a
,但我几乎可以肯定那是错误的。另一种猜测可能是更简单的类型(B x -> H x) -> B Int -> H Int
,例如Int
代表 Ali,但我几乎可以肯定它是错误的。再说一遍:如果你知道,也请告诉我!
最后一点。以这种方式看待事物——遵循证明和程序之间的联系——最终会导致Curry-Howard 同构的深层魔力,但这是一个更高级的话题。(不过真的很酷!)
编辑 1:您还要求提供函数组合的示例。这是一个例子。假设我有一个人的中间名列表。我需要构建所有中间名首字母的列表,但要做到这一点,我首先必须排除每个不存在的中间名。很容易排除中间名为空的每个人;我们只包括中间名不为空的每个人filter (\mn -> not $ null mn) middleNames
。类似地,我们可以很容易地得到某人的中间名首字母head
,所以我们只需要map head filteredMiddleNames
为了得到列表。换句话说,我们有以下代码:
allMiddleInitials :: [Char]
allMiddleInitials = map head $ filter (\mn -> not $ null mn) middleNames
但这是令人恼火的具体情况。我们真的想要一个中间初始生成函数。所以让我们把它变成一个:
getMiddleInitials :: [String] -> [Char]
getMiddleInitials middleNames = map head $ filter (\mn -> not $ null mn) middleNames
现在,让我们看一些有趣的东西。函数map
有类型(a -> b) -> [a] -> [b]
,既然head
有类型[a] -> a
,map head
就有类型[[a]] -> [a]
。同样,filter
有 type (a -> Bool) -> [a] -> [a]
,所以filter (\mn -> not $ null mn)
有 type [a] -> [a]
。因此,我们可以去掉参数,而是写
-- The type is also more general
getFirstElements :: [[a]] -> [a]
getFirstElements = map head . filter (not . null)
你会看到我们有一个额外的组合实例:not
有 type Bool -> Bool
,null
有 type [a] -> Bool
,所以not . null
有 type [a] -> Bool
:它首先检查给定列表是否为空,然后返回是否不是。顺便说一下,这种转换将函数更改为无点样式;也就是说,结果函数没有显式变量。
您还询问了“强绑定”。我认为您指的是.
and$
运算符的优先级(也可能是函数应用程序)。在 Haskell 中,就像在算术中一样,某些运算符的优先级高于其他运算符,因此绑定更紧密。例如,在表达式1 + 2 * 3
中, this 被解析为1 + (2 * 3)
。这是因为在 Haskell 中,以下声明是有效的:
infixl 6 +
infixl 7 *
数字(优先级)越高,调用该运算符的越早,因此该运算符的绑定越紧密。函数应用程序实际上具有无限优先级,因此它绑定得最紧密:表达式f x % g y
将解析(f x) % (g y)
为任何 operator %
。( .
composition) 和$
(application) 运算符具有以下固定性声明:
infixr 9 .
infixr 0 $
优先级范围从 0 到 9,所以这说明.
运算符的绑定比任何其他(函数应用程序除外)都更紧密,并且绑定$
不那么紧密。因此,表达式f . g $ h
将解析为(f . g) $ h
; 事实上,对于大多数运营商来说,f . g % h
将会是(f . g) % h
并且f % g $ h
将会是f % (g $ h)
。(唯一的例外是少数其他零或九个优先级运算符。)