Haskell: Функции, которые иногда возвращают функцию

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

Например:

Function Foo (x)
    If X = 0 Return "Done" 
    Else Return a Function that calls Foo(x-1)
6
задан SamB 3 May 2011 в 04:01
поделиться

7 ответов

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

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

Однако, поскольку в этом случае тип для 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, потому что он ленив, и вещи обычно не оцениваются до того, как они будут использованы.

21
ответ дан 8 December 2019 в 04:07
поделиться

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

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

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

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

3
ответ дан 8 December 2019 в 04:07
поделиться

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

1
ответ дан 8 December 2019 в 04:07
поделиться

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

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

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

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

mean1 :: (Float -> Float)
mean1 = mean3 1 2
1
ответ дан 8 December 2019 в 04:07
поделиться

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

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

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

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

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

Точно так же функция, которая не принимает значения, возвращает [Char]

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

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

> :t main
:: IO ()

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

1
ответ дан 8 December 2019 в 04:07
поделиться
{-# LANGUAGE ExistentialQuantification #-}

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

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

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

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

0
ответ дан 8 December 2019 в 04:07
поделиться
foo x =
    if x<=0 then "Done"
            else foo2 (x)

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

Нашел нетривиальный пример. Кажется, это работает.

-1
ответ дан 8 December 2019 в 04:07
поделиться
Другие вопросы по тегам:

Похожие вопросы: