咖喱函数执行n次的另一个函数

时间:2014-09-21 12:04:50

标签: sml

我正在解决旧考试来练习SML。我发现一个有趣的任务是:编写一个函数repeat,用签名'a - >执行另一个函数。 “一个。

我假设所请求的功能是咖喱功能,并使用o - 运营商:

fun repeat (1, f: 'a->'a) = f
|   repeat (n, f: 'a->'a) = f o repeat (n-1, f);

但是,o运算符在正确的过程中没有正式引入,我想知道如果没有它我怎么写呢?

1 个答案:

答案 0 :(得分:3)

不是那么冗长,而是以某种方式,最明确的,然后是更简洁的解释。

curried函数是获取单个参数的函数。如果表达式具有更多参数,则存在尽可能多的嵌套函数。第一个外层函数得到一个参数,它由一个内层函数组成,它可以由一个内部函数组成,依此类推。可以返回任何这种内部级别函数,而不仅仅是最内部函数,如后面所述(这是一种“部分评估”)。内部函数是“专门的”与外部函数的参数(形式上,参数绑定在一个闭包中)。

我们知道至少有一个函数参数f和整数参数counter。还需要有一个参数seed,以便第一次调用函数f

嵌套的顺序可以是任意的或指定的。如果没有指定,我个人更喜欢将最不同的参数放在外部范围内,而在内部范围内变化最大。在这里,我要说的是,至少从变化到最大变化:fcounter seed

这足以建议模板的开头:

val repeat: ('a -> 'a) -> int -> 'a -> 'a =
   fn f: 'a -> 'a =>
      fn count: int =>
         fn seed: 'a =>
            …

我们已经实施了签名的('a -> 'a) -> int -> 'a部分。仍然是最后一个-> 'a,这意味着要返回'a,它将由内循环进行评估。

循环可能是这种形式(伪代码):

val rec loop = fn i =>
   if condition-to-stop
      then return-something-or-`()`
   else loop (i + 1) or (i - 1)

如果循环要计算某些东西,它将需要一个额外的参数作为累加器,并将该累加器作为其最终结果返回。

实现循环并将其放在上面的curried函数模板中,我们得到:

val repeat: ('a -> 'a) -> int -> 'a -> 'a =
   fn f: 'a -> 'a =>
      fn count: int =>
         fn seed: 'a =>
            let
               val rec loop = fn (counter, x) =>
                  if counter <= 0 then x
                  else loop (counter - 1, f x)
            in
               loop (count, seed)
            end

您了解let … in … end构造吗?

注意counter上的守卫可能会像你一样使用模式,但由于SML的整数可能是负数(SML中没有严格的自然),因此捕获这种情况也更安全,因此{{1而不是模式匹配。里程可能会有所不同,但这不是问题的重点。

与上述相同,使用if … then … else代替fun

val rec

fun repeat (f: 'a -> 'a) (count: int) (seed: 'a): 'a = let fun loop (counter, x) = if counter <= 0 then x else loop (counter - 1, f x) in loop (count, seed) end 注意事项参数不是由repeat分隔的(两者都不是,)。这是使用*编写curried函数的方法(相反,fun不是curry)。将它与同一函数的先前loop版本进行比较。如果没有指定类型且只有名称,则可以省略括号。

用作val参数的测试函数:

f

测试:

val appendAnX = fn s: string => s ^ "x"

Curried函数比获取元组的函数更抽象(这是一个正式的单个参数,因此也是一个curry函数,但这是另一个故事,有点作弊),因为外部函数可以部分应用:

这是部分申请,留下最后一个参数val () = print (repeat appendAnX 5 "Blah:") ,未绑定:

seed

然后可以应用此功能仅指定此val repeatAppendAnXThreeTimes = repeat appendAnX 3

seed

同样,val () = print (repeatAppendAnXThreeTimes "Blah:") counter都可以免费使用:

seed

定义val repeatAppendAnX = repeat appendAnX val () = print (repeatAppendAnX 4 "Blah:") 的另一种方式。将其与上面的其他定义进行比较:

repeatAppendAnXThreeTimes