Haskell: функции, которые иногда возвращают функцию - PullRequest
6 голосов
/ 04 июля 2010

Как написать функцию, которая может возвращать значение или другую функцию?

Например:

Function Foo (x)
    If X = 0 Return "Done" 
    Else Return a Function that calls Foo(x-1)

Ответы [ 8 ]

20 голосов
/ 04 июля 2010

В haskell тип возврата функции может зависеть только от типа ее аргументов и, в случае функций с полиморфными типами возврата, от того, как используется возвращаемое значение. В частности, тип возвращаемого значения функции не может зависеть от значения аргумента.

Другими словами: вы не можете делать то, что хотите напрямую. В случаях, когда вы хотите вернуть один из двух типов, вы можете обычно использовать тип Either a b, который определен как data Either a b = Left a | Right b, что позволяет вам возвращать значение типа a, заключенное в Left или введите b, завернутый в Right. Затем вы можете использовать сопоставление с образцом, чтобы получить значение безопасным для типа способом.

Однако, так как в этом случае тип для b должен быть бесконечным, это не работает, и вы должны определить для этого свой собственный тип оболочки. Вот так:

data MyResult = Str String | Fun ( () -> MyResult)
foo 0 = Str "done"
foo x = Fun (\ () -> foo (x-1))

foo теперь имеет тип Num a => a -> MyResult. Однако каждый раз, когда вы вызываете foo, вы должны выполнить сопоставление с образцом, чтобы увидеть, вернули ли вы Str со строкой внутри или Fun с функцией внутри.

Также обратите внимание, что если вы хотите вернуть функцию, а не значение, чтобы задержать выполнение, в haskell это не имеет смысла, потому что это лениво и вещи обычно не оцениваются до их использования. *

3 голосов
/ 05 июля 2010

Судя по внешнему виду вашего псевдокода, я предполагаю, что вы ожидаете вернуть «нулевую» функцию, которая не принимает аргументов и будет вызывать «Foo (x-1)» при вызове.

Если это так, то, как указано в конце ответа sepp2k, в Haskell есть такая необходимость - это то, что происходит по умолчанию.В частности:

foo x = if x == 0 then "Done"
                  else foo(x-1)

делает точно this: значение, возвращаемое при вызове, скажем, foo(7), - это то, что когда программе нужно, ее значение будет оценивать foo(6).Рекурсивный вызов не будет оцениваться внутри вычисления выражения if.

1 голос
/ 04 июля 2010

Просто следуя замечательному ответу sepp2k.Я думаю, что вам не хватает фундаментальной концепции в Haskell - вы всегда возвращаете функцию.Даже своего рода «значение» является функцией.

Например, откройте ghci и попробуйте:

> :t 5
:: (Num t) => t

Просто функция, которая не требует ввода, возвращаемое значение - Num.

> :t "What  is this?"
:: [Char]

Аналогично, просто функция, которая не принимает значения, возвращает [Char]

«Но это все просто значения! Я не уверен!»

Чтоглавное тогда?(Предположим, вы определили это):

> :t main
:: IO ()

Просто функция, которая возвращает экземпляр IO ().

1 голос
/ 04 июля 2010

Я знаю, что это не отвечает на ваш вопрос напрямую, но я думаю, что вам нужно расширить свое представление о том, что значит "вернуть функцию". Например, функция:

mean3 :: Float -> Float -> Float -> Float
mean3 x y z = (x + y + z) / 3

Можно считать «взятием трех чисел и возвращением числа». Или это можно рассматривать как «функцию, принимающую два числа и возвращающую функцию из числа в число»:

mean3 :: Float -> Float -> (Float -> Float)

mean1 :: (Float -> Float)
mean1 = mean3 1 2
1 голос
/ 04 июля 2010

Вам нужно подумать о типах вашей функции: если Foo имеет тип (Int -> t), что такое t?Он должен вернуть что-то типа t в обоих случаях.Я думаю, что это немного сложно, потому что я не думаю, что t может быть типом String или типом функции (->) в одной и той же функции.

0 голосов
/ 03 мая 2011

Обычно мы пишем это как

foo _ = "Done"

или, бессмысленно,

foo = const "Done"

(Если, конечно, мы действительно хотели получить _|_ для отрицательных чисел; -)

0 голосов
/ 04 июля 2010
{-# LANGUAGE ExistentialQuantification #-}

data MyResult = Str String | forall a. Fun a -- deriving Show

foo 0 = Str "done"
foo x = Fun (\ () -> foo (x-1))

такого рода работы, но вы не можете получить экзистенциальный тип (methinks), поэтому вам нужно вызвать foo следующим образом: (\(Main.Str x) -> x) (Main.foo 0).

Если вы знаете, как получить модуль Mainв фокусе в ghci, пожалуйста, оставьте комментарий.

0 голосов
/ 04 июля 2010
foo x =
    if x<=0 then "Done"
            else foo2 (x)

foo2 x = foo (x-1) ++ foo (x-1)

Нашел нетривиальный пример. Это похоже на работу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...