函数评估在Haskell中如何工作

时间:2018-10-21 02:06:53

标签: haskell

我觉得我对函数式编程的知识有些缺乏,所以我决定上网浏览并遵循教程,以使我在this的第一页上看到它时变得更好

  

“说您有一个不变的数字列表xs = [1,2,3,4,5,6,7,8]并且   一个doubleMe函数,它将每个元素乘以2,然后   返回一个新列表。如果我们想将列表中的数字乘以8   命令式语言,并做了doubleMe(doubleMe(doubleMe(xs))),   可能一次通过列表并进行复制然后返回   它。然后它将再次通过列表两次并返回   结果。”

根据我对函数式编程的了解,这似乎是错误的,让我向您展示原因:

doubleMe = (\x.* 2 x)

如此

doubleMe doubleMe doubleMe xs

beta会减少到:

(\x.* 2 x) doubleMe doubleMe xs ->beta
(* 2 doubleMe) doubleMe xs 
(* 2 (\x.* 2 x)) doubleMe xs ->eta
(\x.* 2 * 2 x) doubleMe  xs ->beta
(* 2 * 2 doubleMe)  xs
(* 2 * 2 (\x.* 2 x)) xs ->eta
(\x.* 2 * 2 * 2 x) xs ->beta
(* 2 * 2 * 2 xs) ->beta
(* 4 * 2 xs) -> beta
(* 8 xs)

这意味着该函数的beta与(\ x。* 8 x)等效

给我的印象是,Haskell编译器在执行之前进行了这种简化,这意味着不,它不会像本教程所建议的那样遍历整个列表3次,只会进行一次。我错了吗?如果是这样,那么Haskell为什么不这样做?当然,它将大大改善性能。

1 个答案:

答案 0 :(得分:4)

我认为您只是误读了该段。它说(强调我的):

  

如果我们想用命令式语言将列表乘以8 ,然后执行for (int i = 0; i < books.length; i++) { System.out.print("Enter book " + (i + 1) + " title: "); String title = input.nextLine(); // NOT HERE. input.nextLine(); System.out.println("Enter book " + (i + 1) + " author's name:"); String name = input.nextLine(); System.out.print("Enter book " + (i + 1) + " price: "); String pr = input.nextLine(); double price = Double.parseDouble(pr); System.out.println("Enter book " + (i + 1) + " author's email: "); String email = input.nextLine(); Author a = new Author(name, email); books[i] = new Book(title, price, a); System.out.println(books[i] + "\n"); } ,则它可能会通过列表一次,然后复制并返回。

>

命令式语言是C或Python之类的语言。不是Haskell。

同一段继续将这种行为与“惰性语言”的行为进行对比:

  

在一种懒惰的语言中……它只会在您真正需要时通过列表。这样,当您想要从懒惰的语言中获取某些东西时,您只需获取一些初始数据并有效地进行转换和修补,使其类似于最终所需的内容。

哪个更接近您的期望。