我刚开始学习Haskell,在考试期间,我被要求回答如果被调用会返回什么成本函数,但我无法理解会发生哪些步骤。我再次参加考试,但我无法理解我应该如何解决这类课程。
任何帮助将不胜感激!
cost = n(twice, inc, 3)
n(h,s,x) = if (x<1) then (h s x) else n(h, (h s), x-1)
inc x = x+1
twice n a = n (n a)
答案 0 :(得分:6)
类型签名在这里确实会有很长的路要走。让我们从最简单的inc
:
inc :: Num a => a -> a
inc x = x + 1
这很容易派生,因为+ 1
的类型为Num a => a -> a
,您可以使用:type (+1)
在GHCi中进行检查。接下来,让我们看一下函数twice
。很明显,传入的n
必须是一个函数,因为它适用于a
和n a
。因为它适用于n a
和a
,所以这两个表达式必须具有相同的类型,并且n
必须只有一个参数,所以我们可以说twice
具有型
twice :: (a -> a) -> a -> a
twice n a = n (n a)
现在我们可以找出n
。它需要一个元组(h, s, x)
作为参数,并以递归方式调用。 h
必须是两个参数的函数,因为它适用于s
和x
,而s
在没有更多上下文的情况下是未知的。 x
由于与Num a => a
和Ord a => a
一起使用,因此必须同时为< 1
和-1
,因此我们可以将签名写为
n :: (Num a, Ord a) => (b -> a -> c, b, a) -> c
n (h, s, x) = if x < 1 then h s x else n (h, h s, x - 1)
请注意,我在这里删除了一些不必要的parens。最后,我们可以找出cost
的类型,它只是n
的返回类型:
cost :: (Num a, Ord a) => a
cost = n (twice, inc, 3)
但这会带来什么回报呢?对于初学者来说,它会重写n
的定义,但twice
,inc
和3
替换为:
if 3 < 1
then twice inc 3
else n (twice, twice inc, 3 - 1)
显然3 < 1
是假的,所以让我们减少n (twice, twice inc, 3 - 1)
:
if 2 < 1
then twice (twice inc) 2
else n (twice, twice (twice inc), 2 - 1)
同样的故事,2 < 1
是假的,所以让我们继续减少:
if 1 < 1
then twice (twice (twice inc)) 1
else n (twice, twice (twice (twice inc)), 1 - 1)
这一步没有什么新内容,再试一次:
if 0 < 1
then twice (twice (twice (twice inc))) 0
else n (twice, twice (twice (twice (twice inc))), 0 - 1)
此处我们有0 < 1
,因此我们选择twice (twice (twice (twice inc))) 2
的分支。要解决此问题,只需将inc
和0
插入twice
的定义中:
twice (twice (twice (twice inc))) 0
= twice (twice (twice (inc . inc))) 0
= twice (twice (inc . inc . inc . inc)) 0
= twice (inc . inc . inc . inc . inc . inc . inc . inc) 0
= (inc.inc.inc.inc.inc.inc.inc.inc.inc.inc.inc.inc.inc.inc.inc.inc) 0
= 16
我们现在不能再减少这个表达了!所以整个减排链都是
cost = n (twice, inc, 3)
= if 3 < 1
then twice inc 3
else n (twice, twice inc, 3 - 1)
= n (twice, twice inc, 2)
= if 2 < 1
then twice (twice inc) 2
else n (twice, twice (twice inc), 2 - 1)
= n (twice, twice (twice inc), 1)
= if 1 < 1
then twice (twice (twice inc)) 1
else n (twice, twice (twice (twice inc)), 1 - 1)
= n (twice, twice (twice (twice inc)), 0)
= if 0 < 1
then twice (twice (twice (twice inc))) 0
else n (twice, twice (twice (twice (twice inc))), 0 - 1)
= twice (twice (twice (twice inc))) 0
= inc (inc 0)
= inc (0 + 1)
= (inc.inc.inc.inc.inc.inc.inc.inc.inc.inc.inc.inc.inc.inc.inc.inc) 0
= 16
(为了保持可读性,我使用twice f = f . f
代替twice f x = f (f x)
,但这些定义是等效的)