7

我试图弄清楚 Haskell 是使用动态范围还是静态范围。我意识到,例如,如果您定义:

let x = 10

然后定义函数

let square x = x*x

您有 2 个不同的“x”,这是否意味着它是动态范围的?如果不是,它使用什么范围,为什么?

此外,Haskell 变量可以有别名(相同内存位置/值的不同名称)吗?

谢谢。

4

9 回答 9

17

Haskell 使用(广义上)与大多数其他语言完全相同的词法作用域。

例如。

x = 10

导致在全局范围内引用的值x,而

square x = x * x

将导致x词法范围限定为函数 square。如果您认为上述形式是一种语法上的精巧,它可能会有所帮助:

square = \ x -> x * x

至于你的另一个问题,我不确定你所说的别名是什么意思

于 2008-12-02T06:09:50.010 回答
7

只回答问题的第二部分:

同一个“内存位置”可以有多个别名,但由于它们都是不可变的,所以大多数时候都没有关系。

愚蠢的例子:

foo x y = x * y
bar z = foo z z

foo调用 frombar时,两者xy显然是相同的值。但由于您不能修改xy,您甚至不会注意到。

于 2008-12-02T10:57:42.050 回答
6

你的说法有些地方不对...

  • Haskell 中没有可变变量,只有定义(或不​​可变变量)
  • 可变内存位置是 Haskell 中不存在的概念

在您的示例中,函数中的x不是10只是square 的一个参数,它可以采用任何值(您可以稍后指定类型)在这种情况下为 10,但只是在这种情况下。

以下是Curt Sampson提供的别名示例:

import Data.IORef

main :: IO ()
main = do x <- newIORef 0         -- write 0 into x
          readIORef x >>= print   -- x contains 0
          let y = x
          readIORef y >>= print   -- y contains 0
          writeIORef x 42         -- write 42 into x
          readIORef y >>= print   -- y contains 42
于 2008-12-02T06:39:59.370 回答
3

由于问题的第一部分已经被其他人回答,这里是第二部分:

我假设aliasing你的意思是one name for another。由于 haskell 是一种函数式语言,并且函数在任何情况下都表现为普通标识符,因此您可以这样做:

y = x

y这将为函数定义一个别名x。请注意,一切都是函数。即使它看起来像一个“变量”,它也只是一个不带参数的空函数。类型的别名如下所示:

type Function = Double -> Double

Function这将为该类型定义一个别名Double -> Double

于 2008-12-02T15:43:32.660 回答
2

在您的示例中,x 的全局定义被 x 的局部定义所掩盖。在 Haskell 中,变量的作用域由源代码的静态读取决定——这称为词法作用域,但可以得到类似于带有隐式参数的动态作用域的东西(但这可能导致一些意外行为(我读过;从不我自己试过))。

于 2008-12-02T15:46:09.830 回答
2

Haskell 使用静态嵌套作用域。与其他具有静态嵌套范围的语言相比,有点令人困惑的是,名称的范围是一个块,其中包括在其定义之前的测试。例如

evens = 0 : map (+1) odds
odds  = map : (+1) evens

在这里,“赔率”这个名称在“偶数”的定义范围内,尽管令人惊讶的事实是“赔率”尚未定义。(该示例定义了两个无限的偶数和奇数列表。)

具有类似范围规则的死语言是 Modula-3。但是 Haskell 有点棘手,因为您可以尝试在同一范围内“重新定义”一个变量,但您只需引入另一个递归方程。对于首先学习 ML 或 Scheme 的人来说,这是一个陷阱:

let x = 2 * n
    x = x + 1   -- watch out!

这是非常好的 ML 或 Scheme let*,但 Haskel 具有 scheme letrec 语义,不受 lambda 值的限制。难怪这是棘手的事情!

于 2008-12-10T09:54:05.137 回答
1

简明扼要地总结其他答案:

  1. 词汇范围
  2. 别名很简单,x = 1; y = x但通常并不重要,因为事物是不可变的。

let您在示例中使用的语法看起来像是在交互式ghci>提示中。交互模式中的所有内容都发生在 IO monad 中,因此那里的事情可能看起来比正常情况更易变。

于 2008-12-03T18:53:51.403 回答
0

好吧,正如我认为人们已经说过的那样,Haskell 没有在大多数其他语言中发现的任何变量,它只有表达式。在您的示例let x = 10中,x 是一个始终计算为 10 的表达式。稍后您实际上无法更改 x 的值,尽管您可以使用范围规则通过将 x 定义为另一个表达式来隐藏它。

于 2008-12-07T20:59:01.100 回答
0

是的,Haskell 有别名。试试这个小程序:

import Data.IORef

main :: IO ()
main = do x <- newIORef 0         -- write 0 into x
          readIORef x >>= print   -- x contains 0
          let y = x
          readIORef y >>= print   -- y contains 0
          writeIORef x 42         -- write 42 into x
          readIORef y >>= print   -- y contains 42
于 2009-06-15T01:44:04.750 回答