Как вы можете написать функцию, которая может либо вернуть значение, либо другую функцию?Haskell: Функции, которые иногда возвращают функцию
Например:
Function Foo (x)
If X = 0 Return "Done"
Else Return a Function that calls Foo(x-1)
Как вы можете написать функцию, которая может либо вернуть значение, либо другую функцию?Haskell: Функции, которые иногда возвращают функцию
Например:
Function Foo (x)
If X = 0 Return "Done"
Else Return a Function that calls Foo(x-1)
В 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
, вам нужно сопоставить шаблон, чтобы узнать, вернули ли вы строку с строкой внутри или Fun с функцией внутри.
Также обратите внимание, что если вы хотите вернуть функцию, а не значение, чтобы отсрочить выполнение, это не имеет смысла в haskell, потому что оно лениво и обычно не оцениваются до их использования.
Итак, нет единого базового типа, как у нас в .NET, или почти есть в Java? –
@ JonathanAllen: Существует не только общий базовый тип, вообще нет подтипирования. – sepp2k
Хотя я должен добавить, что во многих случаях, когда вы полагались на подтипирование в языках OO, параметрический полиморфизм haskell позволяет вам добиться такого же эффекта по-другому. – sepp2k
Вы должны думать о типах вашей функции: если Foo имеет тип (Int -> т), что т? В обоих случаях ему нужно вернуть что-то типа t. Я думаю, что это немного сложно, потому что я не думаю, что t может быть строковым типом или типом функции (->) в той же функции.
Конечно, это может быть - это смысл предложения типа «Либо», предложенного многими людьми. – BMeph
Я знаю, что это не отвечает на ваш вопрос напрямую, но я думаю, вам нужно расширить свое представление о том, что означает «вернуть функцию». Например, функция:
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
Это кажется довольно ограниченным. Что делать, если я не знаю, сколько цифр нужно делать раньше времени? –
@ Джонатан Аллен: сделайте функцию, берущую список. – sdcvvc
foo x =
if x<=0 then "Done"
else foo2 (x)
foo2 x = foo (x-1) ++ foo (x-1)
Найден нетривиальный пример. Кажется, это работает.
Вы все еще применяете функцию, а не возвращаете ее. (Также обратите внимание, что это вызывает бесконечный цикл, если вы вызываете foo с нечетным аргументом или foo2 с четным аргументом (или с отрицательным, но это не ново)). – sepp2k
Теперь я вижу о приложении функции против возврата функции. Платит, чтобы прочитать вопрос. Код, по крайней мере, теперь отредактирован и должен лучше вести себя. – DonnyD
Просто следуйте за великолепным ответом sepp2k. Я думаю, что у вас отсутствует фундаментальная концепция в Haskell - вы всегда возвращаете функцию. Даже «ценность» рода - это функция.
Например, бюст открытым GHCI и попробовать:
> :t 5
:: (Num t) => t
Просто функция, которая не имеет никакого ввода, возвращаемое значение является Num.
> :t "What is this?"
:: [Char]
Аналогично, только функция, которая не имеет никакого значения, возвращается [Char]
«Но это все только ценности! Я не уверен!»
Что же тогда общего? (Предположим, что у вас это определено):
> :t main
:: IO()
Просто функция, возвращающая экземпляр IO().
Я не согласен с этим. Функция - это то, что можно применить. Вы не можете применить строку. Все, что не соответствует типу 'a -> b' (для некоторых a и b), не является функцией. – sepp2k
Значит, это не функция? void foo() {printf ("Hello"); } – Dan
В Haskell он либо будет иметь тип '() -> IO()', и в этом случае это будет функция; или он будет иметь тип 'IO()', и в этом случае это будет значение, которое, грубо говоря, является «действием IO». В C тоже вы * должны * применять его, написав 'foo()'. –
{-# 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)
.
Если вы знаете, как получить главный модуль в фокусе в ghci, пожалуйста, напишите комментарий.
С точки зрения вашего псевдокода, я предполагаю, что вы ожидаете вернуть «нулевую» функцию, то есть такую, которая не принимает никаких аргументов, и вызовет «Foo (x-1)» при вызове.
Если это так, то, как указано в конце ответа sepp2k, в Haskell есть такая необходимость - это то, что происходит по умолчанию. В частности:
foo x = if x == 0 then "Done"
else foo(x-1)
делает точно это: Значение, возвращаемое вызовом, скажем, foo(7)
вещь, что, когда нужна программа, это значение будет оценивать foo(6)
. Рекурсивный вызов не будет оцениваться внутри оценки выражения if
.
За исключением, конечно, что выражение 'if' не будет оцениваться, если результат не требуется ... – SamB
Обычно мы пишем это как
foo _ = "Done"
или бесцельно,
foo = const "Done"
(Если, конечно, мы действительно не хотели, чтобы получить _|_
для отрицательных чисел ;-)
Я не знаком с haskell, но как вы можете использовать функцию, если вы не знаете, что он вернется? – Stephen
На других языках я бы просто вызвал функцию, затем просмотрел результат, чтобы узнать, что он чувствовал, как возвращение. Это ничем не отличается от наличия набора записей в базе данных, где каждый столбец может содержать строку, целое или дату. –
В Haskell вы обычно указываете заранее, с какими типами функция может возвращаться, используя тип алгебраических данных, а затем использовать сопоставление образцов для ее проверки. «Либо», как и в ответе sepp2k, является универсальным способом сделать это для двух возможных результатов. –