Генератор последовательности натуральных чисел - PullRequest
2 голосов
/ 21 октября 2010

Как я понимаю, у Haskell нет глобального состояния, поэтому есть ли способ написать функцию f, которая будет возвращать f (n - 1) + 1, где n - это номер вызова функции и f (1) = 0.

Он не должен принимать никаких аргументов и используется как func f

Prelude> f () 
0
Prelude> f ()
1

Ответы [ 4 ]

5 голосов
/ 21 октября 2010

Без использования трюков, подобных unsafePerform, невозможно определить функцию, которая может быть вызвана так, как вы показали в своем примере.Однако можно определить действие ввода-вывода, которое выполняет то, что вы хотите, и его можно использовать следующим образом:

Prelude> x <- f 
Prelude> x
0
Prelude> x <- f
Prelude> x
1

Вот пример программы, которая делает то, что вы хотите, используя IORefs:

import Data.IORef

main = do counter <- newIORef 0
          let f = do count <- readIORef counter
                     modifyIORef counter (+ 1)
                     return count          
          x <- f
          print x
          x <- f
          print x
4 голосов
/ 21 октября 2010

Вы запрашиваете способ обновления некоторого (возможно, скрытого) состояния при каждом вызове процедуры, чтобы функция возвращала разные результаты при одном и том же вводе.

Очевидно, что это не относительная прозрачность функции, поэтому мы должны добавить что-то в режим чисто по умолчанию в Haskell. Мы добавляем понятия вычисления с помощью монад. Вам просто нужно выбрать нужную вам монадическую среду.

Государственная монада

Самый точный способ - это просто добавить в вашу программу понятие состояния через монаду состояний (не путать с монадой "ST"):

import Control.Monad.State.Strict 

-- a (stateful) procedure, that returns and increments an internal state counter 
f :: State Int Int 
f = do 
    n <- get 
    put (n+1) 
    return n 

-- Call 'f' a bunch of times, print the final state. 
main = print $ execState code 0 
 where 
    code = do f; f; f; f

Теперь у 'f' есть внутренний компонент состояния.

Аналогично, в более богатых средах, таких как IO, допускается State, поэтому вы можете использовать монаду IO (или другую вычислительную среду с подчиненным состоянием).

3 голосов
/ 21 октября 2010

Если вы предпочитаете что-то, вы можете просто набрать из командной строки ghci:

Prelude> :m + Data.IORef
Prelude Data.IORef> n <- newIORef 0
Prelude Data.IORef> let f = do { v <- readIORef n ; writeIORef n (v+1); return v}
Prelude Data.IORef> f
0
Prelude Data.IORef> f
1
Prelude Data.IORef> f
2
Prelude Data.IORef> f
3

Ваш пример хотел вызвать "f ()", но это тот C-ism, которого у Haskell нет. Если вы действительно этого хотите, просто измените определение "f", чтобы начать

let f _ = do {...

"()" определено в Haskell как значение единицы, которое является единственным значением типа единицы "()". Вы можете вызвать «f» с любым аргументом, который вы хотите; это будет проигнорировано.

1 голос
/ 21 октября 2010

Попробуйте что-то вроде этого:

f 1 = 0
f n = f (n-1) + 1

РЕДАКТИРОВАТЬ : Кажется, я неправильно понял ваш вопрос; нет , вы не можете делать что-то подобное в haskell;функции должны быть чистыми .Функция в вашем примере не является чистой

...