Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/EmptyTag/160.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 什么';在函数式语言中,部分求值和函数内联有什么区别?_Haskell_Optimization_Functional Programming_Compiler Optimization_Inlining - Fatal编程技术网

Haskell 什么';在函数式语言中,部分求值和函数内联有什么区别?

Haskell 什么';在函数式语言中,部分求值和函数内联有什么区别?,haskell,optimization,functional-programming,compiler-optimization,inlining,Haskell,Optimization,Functional Programming,Compiler Optimization,Inlining,我知道: 函数内联是用函数定义替换函数调用 部分求值是在编译时对程序的已知(静态)部分求值 在命令式语言(如C)中,运算符和函数是不同的,两者之间存在区别。然而,在Haskell这样的函数语言中,操作符也是函数,这两者之间有什么区别吗 这两者之间的唯一区别是,函数内联可以在程序的某些部分上执行,而部分计算可以在整个程序上执行(即∃vs∀) 这两种优化技术在语义上有什么区别?这两种技术之间有区别 在编译器(甚至预处理器)已知的一组给定运算符和函数上计算常量表达式,这在编译时发生。例如,编译器将p

我知道:

  • 函数内联是用函数定义替换函数调用
  • 部分求值是在编译时对程序的已知(静态)部分求值
  • 在命令式语言(如C)中,运算符和函数是不同的,两者之间存在区别。然而,在Haskell这样的函数语言中,操作符也是函数,这两者之间有什么区别吗

    这两者之间的唯一区别是,函数内联可以在程序的某些部分上执行,而部分计算可以在整个程序上执行(即
    vs


    这两种优化技术在语义上有什么区别?

    这两种技术之间有区别

    • 在编译器(甚至预处理器)已知的一组给定运算符和函数上计算常量表达式,这在编译时发生。例如,编译器将
      print(2*2)
      编译为
      print(4)
      。这决不需要像您所暗示的那样局限于运算符表达式(例如,
      print(sqrt(2.0)
    • 部分求值,这是一个更广泛的概念。编译器可以意识到
      print(myfunc(2))
      可以转换为
      print(c)
      ,其中
      c
      是调用
      myfunc(2)
      的结果。然后它可以(在“专业化时间”)调用
      myfunc(2)
      来确定
      c
      。当然,如果
      myfunc
      有副作用,比如擦自己的硬盘而不是程序用户的硬盘,那么这将出现严重错误。因此编译器需要某种注释或属性来知道何时允许/需要(例如c++11)
    内联是一个不相关的概念。内联函数调用意味着用被调用函数的主体替换调用。此主体不进行计算

    在C这样的命令式语言中,运算符和函数是不同的。然而,在Haskell这样的函数式语言中,运算符也是函数,两者之间有什么区别吗

    这种区别(运算符与函数)纯粹是句法上的,与内联和部分求值之间的区别无关:

    函数调用和带有运算符的表达式都可以内联,并在C中进行编译时计算。编译时计算仅限于一组固定的运算符和函数上的表达式(主要是运算符,但这是历史事故)

    这两个概念都是有意义的,并且在Haskell中是不同的

    • 内联:
      ghc
      具有
      {-#内联f#-}
      ,其中
      f
      不能递归,原因很明显
    • 部分求值:这通常推广到不仅转换基类型的表达式,甚至转换函数,例如将
      map f(map g xs
      )转换为
      map(f.g)xs
      )。它也可以(但不需要)进行内联。是在编译时(明确地)评估程序部分的另一种方法

    因此,您的标题问题的答案是:内联和部分求值之间的差异与函数和运算符之间的差异无关,在函数语言中与在C中几乎相同。由于副作用,部分求值在C中可能更困难(参见上面的擦除硬盘)考虑以下代码:

    map f xs = case xs of [] -> []; (x:xs') -> f x : map f xs'
    f x y = if x % 2 == 0 then y + x / 2 else y + x
    
    main = map (f 3) [1..100]
    
    部分评估后
    f3

    f3 y = if 3 % 2 == 0 then y + 3 / 2 else y + 3
    main = map f3 [1..100]
    
    然后在不断折叠后
    3%2==0

    f3 y = y + 3
    main = map f3 [1..100]
    
    main = map (\y -> y + 3) [1..100]
    

    现在让我们考虑内联<代码> f>代码>:

    注意:
    f3
    中的
    f
    不会内联,因为它不是一个完整的应用程序,但我们可以调整
    f
    的定义以实现它,有关更多详细信息,请参阅

    然后在不断折叠后
    3%2==0

    f3 y = y + 3
    main = map f3 [1..100]
    
    main = map (\y -> y + 3) [1..100]
    
    上面的例子表明,内联和部分评估都可以提供额外的优化机会,但它们仍然有很大的不同:

  • 当特殊版本(
    f3
    在上面的示例中)多次出现时,内联将产生大量重复的代码
  • 当第一个参数不是静态常量时,部分求值仍然有效,但内联将不起作用
  • 第二点,考虑下面的例子:

    main = [f x y | x <- [1..10], y <- [1..100]]
    

    main=[f x y | x从一点研究来看,在我看来,函数内联实际上只是在替换函数定义(模变量名)为了减少函数调用的开销并允许在调用站点进行更多优化,在其调用站点中没有执行任何评估。部分评估的一个示例是将
    60*1000
    转换为
    60000
    ,或者找到一些可以简化为等效表达式的表达式但是更简单的一个。我不完全确定这个位置,所以我不打算把它作为一个答案发表,但这是我的解释。我应该在下面给出我的回答之前问这个问题:为什么你认为在一种运算符也是函数的语言中,内联和部分求值之间的区别会有任何不同?