2014-09-21 65 views
-1

我正在解决旧的考试以练习SML。我发现一件令人感兴趣的任务是:编写一个函数repeat,该函数用签名'a - >'a执行另一个函数。执行n次函数的咖喱函数

我假定所请求的功能是咖喱的功能和使用的o - 运算符:

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

然而,o运营商并没有正式出来课程介绍,我不知道我怎么能不写这个?

+0

(注意倒票不是我)。你不需要使用'o'运算符(尽管这可能是有意义的......就像滥用这个运算符一样),反正不是那样。你必须使用'f'的前一个结果递归。注意问题的文本显示为“执行另一个函数”,而不是“返回函数”,并且您试图在此返回一个函数。这是作业吗? – Hibou57 2014-09-21 12:44:02

+0

@ Hibou57这个问题来自我正在努力练习的旧考试,因为我将在下周三参加考试。 ---我认为咖喱功能比普通版本更具挑战性,所以我尝试了,但没有'o'就解决不了。你能否详细说明你将如何解决它? – mafu 2014-09-21 14:24:41

+1

至于非咖喱版本,我想它会像'fun repeat(x,f:'a - >'a,0)= x | repeat(x,f,n)= repeat(f(x),f,n-1)'。 – mafu 2014-09-21 14:29:15

回答

3

不是那么冗长,而是在某种程度上,最明确的,然后是后面,不详细的解释。

curried函数是获取单个参数的函数。如果一个表达式有更多的参数,那么嵌套的函数就是这么多。第一个外层函数获取一个参数,并且由内层函数构成,它可以由内层函数构成,等等。如此后所述(这是一种“部分评估”),任何这种内部函数都可能被返回,而不仅仅是最内层的函数。内部函数与外部函数的参数(形式上,参数绑定在闭包中)是“专用的”。

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

嵌套顺序可以是任意的或指定的。如果没有指定,我个人更倾向于在外部范围内放置最少变化的参数,而在内部范围内放置最多的参数。在这里,我会说从最少到最不一样:fcounterseed

这已经足以说明一个模板的开头:

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) 

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

实施循环,并把它的咖喱函数模板内部上方,我们可以得到:

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没有严格的自然),这是比较安全的抓住这个情况下也是如此,因此的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的参数没有被,(既不是*)隔开。这是使用fun编写一个咖喱功能的方式(相反,loop不是咖喱)。将其与之前的val版本的相同功能进行比较。如果没有指定类型并且只有名称,则可以省略括号。

的测试功能将被用作f参数:

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

测试:

val() = print (repeat appendAnX 5 "Blah:") 

咖喱功能比函数得到一个元组(这是正式一个参数更抽象的,因此也产生了咖喱味的功能,但这是另一个故事,有点作弊),因为外部功能可能会部分应用:

这是一个部分应用程序,留下的最后一个参数,seed,未结合的:

val repeatAppendAnXThreeTimes = repeat appendAnX 3 

然后该功能可被应用于specifiying仅此seed

val() = print (repeatAppendAnXThreeTimes "Blah:") 

同样,既counterseed可左右自由:

val repeatAppendAnX = repeat appendAnX 
val() = print (repeatAppendAnX 4 "Blah:") 

定义repeatAppendAnXThreeTimes的另一种方法。将其与上面的其他定义进行比较:

val repeatAppendAnXThreeTimes = repeatAppendAnX 3 
val() = print (repeatAppendAnXThreeTimes "Blah:") 
+0

谢谢你这个非常透彻的答案。我需要仔细阅读,直到我完全理解它。鉴于你的结构化方式,我并不感到惊讶,我找不到解决方案,这里有一些神奇的事情发生! – mafu 2014-09-21 17:16:39

+0

@mafu,这不是魔术,那只是关闭。这是lambda微积分的一个重要属性。把它看作符号绑定。理解一个函数可以返回一个函数也很重要,就像在lambda演算中一样,函数是其他表达式之间的表达式。 – Hibou57 2014-09-21 17:46:45