繁体   English   中英

SML / NJ - 使用foldr的一个行长度函数

[英]SML/NJ - One line length function using foldr

我正在尝试创建一个长度函数,类似于已包含在ML中的长度函数。 我的限制是它必须在一行上完成并使用map,foldl或foldr。

现在我的代码行如下所示:

val mylength = foldr ( fn(x,y) => 1+y) 0;

我绝不是ML的专家,但到目前为止,我的理由是这样的:

根据我的理解,foldr将从列表中的最后一项开始,将其作为我函数中的x参数传递,并使用0作为初始y值。 然后应该在y值上加1,基本上忽略x。 理论上,我相信这会给我我的总长度。 但是我收到以下错误:

 stdIn:136.5-136.37 Warning: type vars not generalized because of
   value restriction are instantiated to dummy types (X1,X2,...)
 val mylength = fn : ?.X1 list -> int

我的一个大问题是如何以一种可以接受任何类型列表的方式来创建这个函数。

如果有人可以提供一些关于如何处理这个问题的建议我会很感激,也许我还没有完全围绕ML的编程风格。

你的功能本质上是正确的。 根据您使用的解释器,它将接受给定的代码或拒绝它。 例如,在CloudML上运行代码就可以了。 要避免此问题,请将其定义为如下函数:

fun mylength l = foldr ( fn(x,y) => 1+y) 0 l;

来自华盛顿大学的Daniel Grossman在他的一个课程中解释说,这个错误与可变引用有关。 遗憾的是,我无法回想起他在哪一课中提到这一点。

您可以同时考虑以下内容:

给出foldlfoldr一些标准定义

fun foldr f e []      = e
  | foldr f e (x::xr) = f(x, foldr f e xr);

fun foldl f e []      = e
  | foldl f e (x::xr) = foldl f (f(x, e)) xr;

可以手动将您的功能应用到一个简短的列表,看看术语重写是如何展开的:

foldr (fn(_,y) => 1+y) 0 [5,6,7]
(fn(_,y) => 1+y) (5,foldr (fn(_,y) => 1+y) 0 [6,7])
(fn(_,y) => 1+y) (5,(fn(_,y) => 1+y) (6,foldr (fn(_,y) => 1+y) 0 [7]))
(fn(_,y) => 1+y) (5,(fn(_,y) => 1+y) (6,(fn(_,y) => 1+y) (7,foldr (fn(_,y) => 1+y) 0 [])))
(fn(_,y) => 1+y) (5,(fn(_,y) => 1+y) (6,(fn(_,y) => 1+y) (7,0)))
(fn(_,y) => 1+y) (5,(fn(_,y) => 1+y) (6,1))
(fn(_,y) => 1+y) (5,2)
3

使用foldr您可以看到首先解析元素7 (它从右向左折叠),并且表达式的长度(以及堆栈内存)与列表成比例增长。 使用foldl您可以看到5首先被解析(它从左到右折叠)并且表达式的长度是常量。 在这两种情况下,匿名函数的参数的第一部分都将被丢弃。

foldl (fn(_,y) => 1+y) 0 [5,6,7]
foldl (fn(_,y) => 1+y) ((fn(_,y) => 1+y)(5, 0)) [6,7]
foldl (fn(_,y) => 1+y) 1 [6,7]
foldl (fn(_,y) => 1+y) ((fn(_,y) => 1+y)(6, 1)) [7]
foldl (fn(_,y) => 1+y) 2 [7]
foldl (fn(_,y) => 1+y) ((fn(_,y) => 1+y)(7, 2)) []
foldl (fn(_,y) => 1+y) 3 []
3

不可否认,lambdas使整个事情变得混乱。 鉴于定义

fun plus1(_,y) = 1+y

以下重写是等效的,但更清晰。

foldr plus1 0 [5,6,7]
plus1 (5, foldr plus1 0 [6,7])
plus1 (5, plus1 (6, foldr plus1 0 [7]))
plus1 (5, plus1 (6, plus1 (7, foldr plus1 0 [])))
plus1 (5, plus1 (6, plus1 (7, 0)))
plus1 (5, plus1 (6, 1))
plus1 (5, 2)
3

foldl plus1 0 [5,6,7]
foldl plus1 (plus1 (5,0)) [6,7]
foldl plus1 1 [6,7]
foldl plus1 (plus1 (6,1)) [7]
foldl plus1 2 [7]
foldl plus1 (plus1 (7,2)) []
foldl plus1 3 []
3

一个稍微长一点的解决方案(我通过尝试思考map是如何相关的而得出的):

fun len xs = (foldr op+ 0 o map (fn x => 1)) xs;

这里o是组合运算符。 我想写

val len  = foldr op+ 0 o map (fn x => 1); 

这样可以模仿Haskell中流行的那种无点样式,但遇到了与原始定义相同的值限制。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM