在Ocaml中处理单元

在Ocaml中处理单元,ocaml,Ocaml,因此我有一个函数r,它应该对列表中的每个元素应用一个函数,只要它满足给定的谓词,然后返回该列表。i、 e let p x = x > 2;; let f x = x+1;; r p f [1;2] => [] 我正在使用一个map函数,它将一个函数应用于列表中的每个元素,然后返回该列表。因此,我对r的实现如下 let r f p l = map f (map (fun x -> if (p x) then x) l );; 但是,如果我试图调用r,就像上面的例子中那样,我

因此我有一个函数
r
,它应该对列表中的每个元素应用一个函数,只要它满足给定的谓词,然后返回该列表。i、 e

let p x = x > 2;;
let f x = x+1;;

r p f [1;2] => []
我正在使用一个
map
函数,它将一个函数应用于列表中的每个元素,然后返回该列表。因此,我对
r
的实现如下

let r f p l = map f (map (fun x -> if (p x) then x) l );;

但是,如果我试图调用
r
,就像上面的例子中那样,我会得到一个类型错误,因为f和p是int的表达式,它应该是units的表达式。哪里出错了?

函数将函数应用于列表的每个元素,并返回结果列表。它总是返回一个与输入列表长度相等的列表。所以你的问题陈述没有完全的意义

在较低的级别,表达式
if(px)then x
只有在x具有类型单位时才是合法的。也就是说,
如果b那么e
的含义与
如果b那么e else()
的含义相同,如果
的两侧必须是相同的类型


如果要返回与输入列表长度不同的列表,则需要使用折叠函数而不是
map

函数
map
函数将函数应用于列表的每个元素并返回结果列表。它总是返回一个与输入列表长度相等的列表。所以你的问题陈述没有完全的意义

在较低的级别,表达式
if(px)then x
只有在x具有类型单位时才是合法的。也就是说,
如果b那么e
的含义与
如果b那么e else()
的含义相同,如果
的两侧必须是相同的类型


如果要返回与输入列表长度不同的列表,则需要使用折叠函数,而不是
map

首先让我解释一下,为什么
单元
起作用

在OCaml
if/then/else
中,if/then/else不是一个语句,它是一个表达式,类似于类C语言中的三元运算符或Python中的条件表达式。比意味着,作为一个表达式,它总是必须有一个值。除非else分支的值不重要并且编译器总是知道,否则不能只为true分支提供一个表达式,而忽略else分支。后者仅适用于
单元
类型。因为这个类型只有一个值,所以如果您的true分支返回一个unit类型的值,编译器已经知道返回false分支的内容。这就是为什么可以省略表达式中计算为单位的其他部分。省略else部分是编译器满意的证据,证明整个表达式具有类型
单元
。这意味着,在表达式
if(px)then x
中,编译器决定
x
具有类型
单元
,因为没有其他部分

现在开始执行任务<代码>映射
必须为列表中的每个元素返回一个值。它不能跳过、重新排列或更改结构。为此,还有其他高阶函数称为
filter\u map
concat\u map
filter
,等等

但是,让我们试着做点什么,不要离开原来的措辞。回到你的例子,我们需要在另一部分做点什么。我们应该返回什么来指定没有价值?我们可以返回
None
,这是一个类型为
option
的值,例如

if p x then Some x else None
请注意,我们还需要将then零件提升到
选项
类型。因此,我们将有一个类型为“选项列表”的列表。然后我们需要过滤它,删除
None
s

另一种选择是返回空列表(也称为
nil
),而不是
None

if p x then [x] else []
然后我们将有一个
'列表
,它可以通过
concat
操作轻松地转换为
'列表
。此外,我们可以注意到,不需要创建中间列表,我们可以就地应用
f
(也就是说,这里有一个优化落叶的机会):

最后,我们有:

let r f p l = concat (map (fun x -> if p x then [f x] else []) l)
稍后,您将发现,
选项
列表
都是单子,而这个带有
映射
concat
的技巧实际上是所有单子的核心操作,称为
绑定
,表示为
>=
。定义此运算符后,我们可以更优雅地编写
r
函数:

let r f p l = l >>= fun x -> if p x then [f x] else []
其中可以实现
bind
操作符(效率低下),如下所示

但这一切都是功能性的胡言乱语,在现实编程中,最好使用
fold_left
(正如Jeffrey所建议的那样),并将结果累积到一个辅助列表中,而不要忘记将其反转:

let r f p l = rev (fold_left (fun xs x -> if p x then f x :: xs else xs) [] l)

首先让我解释一下,为什么
unit
起作用

在OCaml
if/then/else
中,if/then/else不是一个语句,它是一个表达式,类似于类C语言中的三元运算符或Python中的条件表达式。比意味着,作为一个表达式,它总是必须有一个值。除非else分支的值不重要并且编译器总是知道,否则不能只为true分支提供一个表达式,而忽略else分支。后者仅适用于
单元
类型。因为这个类型只有一个值,所以如果您的true分支返回一个unit类型的值,编译器已经知道返回false分支的内容。这就是为什么可以省略表达式中计算为单位的其他部分。省略else部分是编译器满意的证据,证明整个表达式具有类型
单元
。这意味着,在表达式
if(px)then x
中,编译器决定
x
具有类型
单元
,因为没有其他部分

现在开始执行任务<代码>映射
必须为列表中的每个元素返回一个值。它不能跳过、重新排列或更改结构。
let (>>=) x f = concat (map f x) 
let r f p l = rev (fold_left (fun xs x -> if p x then f x :: xs else xs) [] l)