我正在解决旧的考试以练习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
运营商并没有正式出来课程介绍,我不知道我怎么能不写这个?
我正在解决旧的考试以练习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
运营商并没有正式出来课程介绍,我不知道我怎么能不写这个?
不是那么冗长,而是在某种程度上,最明确的,然后是后面,不详细的解释。
curried函数是获取单个参数的函数。如果一个表达式有更多的参数,那么嵌套的函数就是这么多。第一个外层函数获取一个参数,并且由内层函数构成,它可以由内层函数构成,等等。如此后所述(这是一种“部分评估”),任何这种内部函数都可能被返回,而不仅仅是最内层的函数。内部函数与外部函数的参数(形式上,参数绑定在闭包中)是“专用的”。
我们知道至少有一个函数参数f
和整数参数counter
。还需要有一个参数seed
,第一次调用函数f
。
嵌套顺序可以是任意的或指定的。如果没有指定,我个人更倾向于在外部范围内放置最少变化的参数,而在内部范围内放置最多的参数。在这里,我会说从最少到最不一样:f
,counter
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)
如果循环计算的东西,它需要充当累加器一个额外的参数,并且将返回累加器作为其最终结果。
实施循环,并把它的咖喱函数模板内部上方,我们可以得到:
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:")
同样,既counter
和seed
可左右自由:
val repeatAppendAnX = repeat appendAnX
val() = print (repeatAppendAnX 4 "Blah:")
定义repeatAppendAnXThreeTimes
的另一种方法。将其与上面的其他定义进行比较:
val repeatAppendAnXThreeTimes = repeatAppendAnX 3
val() = print (repeatAppendAnXThreeTimes "Blah:")
(注意倒票不是我)。你不需要使用'o'运算符(尽管这可能是有意义的......就像滥用这个运算符一样),反正不是那样。你必须使用'f'的前一个结果递归。注意问题的文本显示为“执行另一个函数”,而不是“返回函数”,并且您试图在此返回一个函数。这是作业吗? – Hibou57 2014-09-21 12:44:02
@ Hibou57这个问题来自我正在努力练习的旧考试,因为我将在下周三参加考试。 ---我认为咖喱功能比普通版本更具挑战性,所以我尝试了,但没有'o'就解决不了。你能否详细说明你将如何解决它? – mafu 2014-09-21 14:24:41
至于非咖喱版本,我想它会像'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