2010-07-04 3 views
5

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

Например:

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

Я не знаком с haskell, но как вы можете использовать функцию, если вы не знаете, что он вернется? – Stephen

+0

На других языках я бы просто вызвал функцию, затем просмотрел результат, чтобы узнать, что он чувствовал, как возвращение. Это ничем не отличается от наличия набора записей в базе данных, где каждый столбец может содержать строку, целое или дату. –

+4

В Haskell вы обычно указываете заранее, с какими типами функция может возвращаться, используя тип алгебраических данных, а затем использовать сопоставление образцов для ее проверки. «Либо», как и в ответе sepp2k, является универсальным способом сделать это для двух возможных результатов. –

ответ

20

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

+3

Итак, нет единого базового типа, как у нас в .NET, или почти есть в Java? –

+11

@ JonathanAllen: Существует не только общий базовый тип, вообще нет подтипирования. – sepp2k

+9

Хотя я должен добавить, что во многих случаях, когда вы полагались на подтипирование в языках OO, параметрический полиморфизм haskell позволяет вам добиться такого же эффекта по-другому. – sepp2k

1

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

+0

Конечно, это может быть - это смысл предложения типа «Либо», предложенного многими людьми. – BMeph

1

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

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 
+0

Это кажется довольно ограниченным. Что делать, если я не знаю, сколько цифр нужно делать раньше времени? –

+5

@ Джонатан Аллен: сделайте функцию, берущую список. – sdcvvc

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

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

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

+2

Вы все еще применяете функцию, а не возвращаете ее. (Также обратите внимание, что это вызывает бесконечный цикл, если вы вызываете foo с нечетным аргументом или foo2 с четным аргументом (или с отрицательным, но это не ново)). – sepp2k

+0

Теперь я вижу о приложении функции против возврата функции. Платит, чтобы прочитать вопрос. Код, по крайней мере, теперь отредактирован и должен лучше вести себя. – DonnyD

1

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

Например, бюст открытым GHCI и попробовать:

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

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

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

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

«Но это все только ценности! Я не уверен!»

Что же тогда общего? (Предположим, что у вас это определено):

> :t main 
:: IO() 

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

+6

Я не согласен с этим. Функция - это то, что можно применить. Вы не можете применить строку. Все, что не соответствует типу 'a -> b' (для некоторых a и b), не является функцией. – sepp2k

+1

Значит, это не функция? void foo() {printf ("Hello"); } – Dan

+0

В Haskell он либо будет иметь тип '() -> IO()', и в этом случае это будет функция; или он будет иметь тип 'IO()', и в этом случае это будет значение, которое, грубо говоря, является «действием IO». В C тоже вы * должны * применять его, написав 'foo()'. –

0
{-# 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, пожалуйста, напишите комментарий.

3

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

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

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

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

+0

За исключением, конечно, что выражение 'if' не будет оцениваться, если результат не требуется ... – SamB

0

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

foo _ = "Done" 

или бесцельно,

foo = const "Done" 

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

Смежные вопросы