обзор, но очень в функциональном программировании - PullRequest
1 голос
/ 23 сентября 2010

Как выглядит очень общая функция в функциональном программировании?

Кто-то сказал: «У нас нет объектов, но у нас есть функции более высокого порядка».Заменяют ли функции более высокого порядка объекты?

При программировании объектно-ориентированных приложений я стараюсь много раз переходить от более общего к более подробному представлению.Если я попытаюсь сделать это в функциональном программировании, понадобится ли мне множество функций более высокого порядка?

Ответы [ 3 ]

11 голосов
/ 23 сентября 2010

Этот ответ ориентирован скорее на Haskell, чем на Lisp, потому что, хотя в lisp есть функции более высокого порядка, идиоматический lisp может быть и часто очень объектно-ориентирован.

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

В общем, абстрактные типы данных «заменяют» объекты, в том смысле, что обычно вы используете объект для связки нескольких связанных данных внапример, Java или Python, и вы объявляете тип данных для таких вещей в Haskell или ML.

Однако объекты также связывают поведение с данными.Таким образом, объект класса имеет данные, но также и функции, которые могут обращаться к этим данным и изменять их.В функциональном стиле вы просто объявляете функции для вашего типа данных за пределами этого типа данных.Тогда инкапсуляция обеспечивается либо модулями, либо использованием замыканий.

В последнем пункте - замыкания и объекты являются двойственными, хотя выражать их как таковые не обязательно.В вики шаблонов портленда есть некоторые очень старые обсуждения этого вопроса: http://c2.com/cgi/wiki?ClosuresAndObjectsAreEquivalent.

О, и пример из oleg: http://okmij.org/ftp/Scheme/oop-in-fp.txt.

Игнорирование классов типов (которые необходимы для идиоматического Хаскелла), и сосредоточив внимание только на основном функциональном программировании, вот набросок другого подхода к тому, что было бы сделано с наследованием в ОО-языке.Функция foo использует некоторый объект, который реализует интерфейс A, и некоторый объект, который реализует интерфейс B, чтобы произвести некоторый Double.С функциями более высокого порядка вы, возможно, имели бы сигнатуру типа fooGen :: (a -> Double -> Double) -> (b -> String -> Double) -> a -> b -> Double.

Эта сигнатура говорит, что fooGen переводит функцию из некоторого a и Double в другой Double, а также функцию некоторого bи String to Double, а затем он принимает a и ab, и, наконец, возвращает Double.

Так что теперь вы можете передать «интерфейс» отдельно от конкретных значений через частичное применение, объявивНапример, fooSpecialized = fooGen funcOnA funcOnB.

С помощью классов типов вы можете абстрагироваться от конкретной передачи "реализации интерфейса" (или, на более хорошем языке, словаря) и объявить foo :: HasSomeFunc a, HasSomeOtherFunc b => a -> b -> Double.Вы можете думать о вещах с левой стороны => как о свободном объявлении интерфейсов, которые должны реализовывать ваши конкретные типы a и b.

Это все неуклюжий и частичный ответКонечно, на очень общий вопрос.

4 голосов
/ 23 сентября 2010

Сначала ответы

Кто-то сказал: «У нас нет объектов, но у нас есть функции более высокого порядка». Заменяют ли функции высшего порядка объекты?

Если вы имеете в виду, могут ли функции более высокого порядка содержать скрытое состояние, тогда да. Функции, определенные внутри других функций, могут захватывать некоторую информацию из своей области и, если она будет возвращена во внешний мир, сохранят эту информацию. Вот что такое замыкания.

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

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

Если я попытаюсь сделать это в функциональном программировании, понадобится ли мне много функций более высокого порядка?

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

Например, map - это HOF. Его первый аргумент - другая функция. То, что вы считаете в императивном языке циклом «для каждого элемента в коллекции: примените некоторую функцию, сохраните результат», на функциональном языке будет «отобразить функцию на коллекцию и получить новую коллекцию результатов». , Складки являются еще одним примером HOF. Таким образом, большинство циклов императивного языка могут быть переведены на вызовы функций более высокого порядка на функциональном языке. Это должно дать понять, как часто вы можете их использовать.

обзор, но очень сложный в функциональном программировании

Это хорошее место для начала: Функциональное программирование .

Пример инкапсуляции "состояния":

f = let x = 3
    in  let withX y = x + y
        in  withX

Теперь f - это то же самое, что и withX, функция, которая «запоминает», что x = 3. Когда мы используем f, нам нужно предоставить только один аргумент, y, и он будет суммировать его с "запомненным" значением x (3).

Это должно вывести 3, а затем [4, 5, 6]:

main = do
  print $ f 0
  print $ map f [1..3]

Мы не передаем 3 в качестве аргумента f, он "запоминает" 3 из закрытия выше, и мы можем передать f в качестве параметра map, который в данном случае является HOF.

Таким образом, функции могут инкапсулировать состояние.

Пример "изменения" переменной

Как я уже говорил выше, в функциональном программировании состояние не является изменяемым. Так, если вы хотите, скажем, применить к значению операцию f, сохранить результат, а затем применить операцию g, на функциональном языке вы бы выразили ее с помощью промежуточной переменной, которая содержит результат применения f и затем примените g к нему, чтобы вычислить новое значение. Обратите внимание, что вы не «перезаписываете» исходное значение x0:

applyTwo first second x0 =
  let x1 = first x0
  in  second x1

Но на самом деле его можно написать короче, потому что это просто набор функций:

applyTwo' f g = g . f

Или вы можете обобщить этот подход и написать функцию, которая будет применять любое количество функций:

applyAll []          = id       -- don't do anything if there are no functions to apply
applyAll (f:otherFs) = applyAll otherFs . f

Обратите внимание, что applyTwo и applyAll теперь являются функциями более высокого порядка. Конечно, они не заменяют объекты, но позволяют избежать изменяемого состояния.

Вот как они используются:

ghci> applyTwo (+1) (*10) 2
30
ghci> applyAll [(+1), (*10)] 2
30
2 голосов
/ 24 сентября 2010

Это все программирование;одни и те же шаблоны появляются снова и снова.Вы могли бы написать что-то вроде этого на своем любимом ОО-языке:

role Person {
   has 'firstname' => ( isa => 'Str' );
   has 'lastname'  => ( isa => 'Str' );
}

class Employee does Person {
   has 'manager' => ( isa => 'Employee'   );
   has 'reports' => ( isa => '[Employee]' );
}

В то время как в Хаскеле вы написали бы:

class Person a where
    firstname :: a -> String
    lastname  :: a -> String

data Employee = Employee { firstName :: String
                         , lastName  :: String
                         , manager   :: Employee
                         , reports   :: [Employee]
                         }

instance Person Employee where
    firstname = firstName
    lastname  = lastName

Люди слишком беспокоятся о том, что отличается, а не осознавая, чтобольшинство вещей одинаковы.

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