2017-04-20 140 views
10

我不知道如何重新分配函数中的变量。如何在Haskell中的函数中重新分配变量?

例如,

elephant = 0 

function x = elephant = x 

为什么不这项工作?

+1

你要定义一个变量还是要重新 - 分配一个现有的变量?我在问,因为你的例子看起来像你正试图改变全局'elephant'变量的值。 – sepp2k

+0

即时尝试重新分配一个现有的变量。 – Fl4mer

+37

在这两种情况下,我认为你正在击中S.O.太快了,需要学习更多的语法和语言原语。我建议你通过阅读来学习更多的Haskell。例如,[wikibooks](https://en.wikibooks.org/wiki/Haskell)和“函数式编程工艺”是开始的好地方。 –

回答

43

im trying to re-assign an existing variable

你不能在Haskell中做到这一点。你可以通过使用IORef来完成某些事情,但这很少是问题的正确解决方案 - 当然不是初学者可能遇到的情况。

相反,您应该重新设计您的程序逻辑,以便它不需要可变变量的功能。

+5

在这一点上,我怀疑有任何程序逻辑来重新设计。 OP只想知道如何处理Haskell中的其他语言。 –

106

Haskell是一个伟大的imperative语言,编写可以重新分配状态的程序是一个非常有趣的高级话题!这绝对不是你现在想要的方法,但有一天会回来

它需要一点努力去定义一个模拟全局可变变量的环境。然而,一旦你掌握了它,类型的精确性就变得非常方便了。

我们将使用lensmtl库。

{-# LANGUAGE TemplateHaskell #-} 

import Control.Lens 
import Control.Monad.State 

大家都知道,大象是整数。

type Elephant = Integer 

你想要一个程序,其全局可变状态有一头大象。首先让我们来定义一下大象的含义。 Lens很好地捕捉了这个概念。现在

class HasElephant a 
    where 
    elephant :: Lens' a Elephant 

,我们可以定义function,它分配一个新的价值elephant

function :: (MonadState s m, HasElephant s) => Elephant -> m() 
function x = 
    elephant .= x 

约束MonadState s mHasElephant s都在说我们的程序必须能够持有某种类型s的可变状态,类型s必须有大象。

我们还定义一个打印大象的程序。

printElephant :: (MonadState s m, HasElephant s, MonadIO m) => m() 
printElephant = 
    use elephant >>= (liftIO . print) 

这个程序执行I/O(印刷),所以我们有一个附加的约束MonadIO m,说我们的节目类型m必须能够做I/O。

非洲森林大象(Loxodonta cyclotis)是一种在刚果盆地发现的森林栖息物种。

data Congo = Congo 
    { _congoElephant :: Elephant 
    } 
makeLenses ''Congo 

我们必须定义Congo有一头大象的方式。

instance HasElephant Congo 
    where 
    elephant = congoElephant 

现在我们可以编写一个示例程序。我们的程序将打印elephant的值,然后更改elephant的值,然后再次打印。

main' :: StateT Congo IO() 
main' = 
    do 
    printElephant 
    function 2 
    printElephant 

然后我们可以运行这个程序。

main :: IO() 
main = Congo 0 & runStateT main' & void 

输出是:

0 
2 
+24

虽然这些都是很好的事情要知道和使用一般,我觉得这可能不是最合适的时候谈论'StateT',monad变压器,类型和镜头......如果有人不知道这方面的哈斯克尔(没有像C这样的语言的方式赋值)并且以前没有遇到过这种差异,我怀疑他们还没有这方面的背景,甚至可能会吓跑他们(我*可能*是错的当然,但我有一个非常强烈的怀疑......)。 –

+35

@DavidYoung我不指望我的答案会提供任何即时效用的提问者。其他人已经这样做了。这个答案在这里,因为我一直相信,偶尔瞥见一些你还不能理解的东西是有价值的,从你脚下的路径向上看,并注视着山上的云彩形状。 –

+1

这是一个公平点。 –

24

Haskell是在函数式编程世界和函数式编程的领导者通常被称为“编程不分配。”这几乎是功能编程的整个不使用分配。一旦你使用它,你就不再以“功能”的方式来做它了。当然有时候,FP会尽量减少这些时间。

所以,要回答你的问题,“为什么不工作?”首先,语法不正确。 =并不意味着在Haskell中赋值。它将一个名称绑定到一个表达式。你不能这样做两次(在相同的范围内)。换句话说,“变量”是不可变的(就像在数学中一样)。其次,突变是一种副作用行为,Haskell将这些行为视为必须在IO世界中完成的不纯行为。

我可以告诉你如何实际上变异在Haskell中的引用,但我不认为这就是你需要在这一点上。

10

到可变x绑定到值v最原始的方法是编写的函数采取x作为参数,并传递给v该功能。

这有时可以用来“模拟”可变变量的效果。

例如,势在必行代码

// sum 0..100 
i = s = 0; 
while (i <= 100) { 
    s = s+i; 
    i++; 
} 
return s; 

成为

final_s = f 0 0 -- the initial values 
    where 
    f i s | i <=100 = f (i+1) (s+i) // increment i, augment s 
     | otherwise = s    // return s at the end 

上面的代码是不漂亮FP码,但至少它是足够接近的命令性代码,使其能够当场连接。


最后题外话:

当一个第一通知这一点,它通常是引诱到落入Blub paradox。人们可以很容易想到:“什么!Haskell需要所有的东西来模拟一个简单的任务?如果在语言Blub分配是微不足道的,并且模拟Haskell需要这么多努力,那么显然Blub比Haskell好得多!”。这将是一个完美的Blub悖论案例:当一个Blub程序员转向另一种语言时,他们立即察觉到Blub不能直接翻译的内容,并且没有注意到新语言的所有其他功能并不存在泡壳。 他们的思想现在在“Blub”中思考,它需要付出很大的努力才能适应新的模型。

几乎与此相反,学习 FP和命令式编程是有用的,因为当仅用于其中的一种时,学习其他范式并非微不足道。如果他们之间的差距很小,那么为了解决同样的问题而学习两种接近的方法是不值得的。

2

一般来说这不起作用,因为您通常会进行不可变的声明,而不是指定一系列操作。你可以这样做:

elephant = 3 
main = print elephant 

但你也可以这样做:

main = print elephant 
elephant = 3 

由于代码不指定执行顺序,也没有办法解释多项任务作为不是错误以外的任何。

如果你想指定的一系列操作,使用符号:

main = do 
    let elephant = 0 
    print elephant 
    let elephant = 1 
    print elephant 
    let elephant = 2 
    print elephant 

的代码做块顺序执行,这样可以有效地重新分配变量的方式,您可以在大多数编程语言中。

请注意,这段代码实际上只是为大象创建了一个新的绑定。旧的价值依然存在:

main = do 
    let elephant = 1 
    print elephant 
    let printElephant = print elephant 
    let elephant = 2 
    print elephant 
    printElephant 

因为printElephant功能我定义依然采用大象的旧值,这样打印:

1 
2 
1 
+3

你确实可以做到这一点,但是强调'let elephant = 1'和'let elephant = 2'确实不**重新分配变量'elephant'很重要。相反,他们只是引入了一个_new_变量,该变量恰好也被称为'大象'。然后,“印象大象”选择最近的一次出现(更确切地说,是_narrowest共享范围内的那个),但在这个范围之外,老大象始终保持相同的值。我建议这不应该在实际的Haskell代码中完成(实际上'ghc-Wall'会警告它)。 – leftaroundabout

+0

@leftaroundabout你说得对,我加了一点来说明不同之处。 此外,我知道这不是建议,但问题违背了语言所代表的一切。用一些可以推荐的东西很难回答这个问题。 –