Вложенные функции: неправильное использование побочных эффектов? - PullRequest
5 голосов
/ 22 ноября 2008

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

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

Какой желаемый способ продолжить?

Спасибо!

Ответы [ 3 ]

3 голосов
/ 22 ноября 2008

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

Вот небольшой пример на схеме:

(define (foo a)
  (define (bar b)
    (+ a b))      ; getting a from outer scope, not purely functional
  (bar 3))

(define (foo a)
  (define (bar a b)
    (+ a b))      ; getting a from function parameters, purely functional
  (bar a 3))


(define (bar a b) ; since this is purely functional, we can remove it from its
  (+ a b))        ; environment and it still works

(define (foo a)
  (bar a 3))

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

2 голосов
/ 22 ноября 2008

Вложенные функции - отличный способ разделить труд во многих функциях. Это не совсем "побочный эффект"; если это помогает, думайте о захваченных переменных как о неявных параметрах.

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

let factorial n =
    let rec facHelper p n =
        if n = 1 then p else facHelper (p*n) (n-1)
    in
    facHelper 1 n

В этом случае не имеет смысла объявлять такую ​​функцию, как facHelper, глобально, поскольку пользователям не нужно беспокоиться о параметре p.

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

1 голос
/ 22 ноября 2008

Рассмотрим следующий (надуманный) фрагмент Haskell:

putLines :: [String] -> IO ()
putLines lines = putStr string
    where string = concat lines

string - локально связанная именованная константа. Но разве это не функция, не принимающая аргументов, которая закрывается над lines и, следовательно, является косвенно прозрачной? (В Haskell константы и нулевые функции действительно неразличимы!) Считаете ли вы приведенный выше код «побочным эффектом» или не функциональным из-за этого?

...