嵌套功能:副作用使用不当?

时间:2021-06-13 22:04:30

I'm learning functional programming, and have tried to solve a couple problems in a functional style. One thing I experienced, while dividing up my problem into functions, was it seemed I had two options: use several disparate functions with similar parameter lists, or using nested functions which, as closures, can simply refer to bindings in the parent function.

我正在学习函数式编程,并尝试以函数式方式解决几个问题。在将问题分解为函数时,我遇到的一件事是,我似乎有两个选择:使用具有相似参数列表的几个不同的函数,或者使用嵌套函数,作为闭包,它们可以简单地引用父函数中的绑定。

Though I ended up going with the second approach, because it made function calls smaller and it seemed to "feel" better, from my reading it seems like I may be missing one of the main points of functional programming, in that this seems "side-effecty"? Now granted, these nested functions cannot modify the outer bindings, as the language I was using prevents that, but if you look at each individual inner function, you can't say "given the same parameters, this function will return the same results" because they do use the variables from the parent scope... am I right?

虽然我最终采用第二种方法,因为它使函数调用更小并且似乎“感觉”更好,从我的阅读中看起来似乎我可能缺少函数式编程的一个要点,因为这似乎是“侧面” -effecty“?现在被授予,这些嵌套函数不能修改外部绑定,因为我使用的语言阻止了这一点,但如果你看一下每个单独的内部函数,你不能说“给定相同的参数,这个函数将返回相同的结果”因为他们确实使用了父范围的变量......我是对的吗?

What is the desirable way to proceed?

什么是理想的进行方式?

Thanks!

3 个解决方案

#1


2  

Nesting functions is an excellent way to divide up the labor in many functions. It's not really "side-effecty"; if it helps, think of the captured variables as implicit parameters.

嵌套功能是在许多功能中分工的好方法。这不是真正的“副作用”;如果有帮助,可以将捕获的变量视为隐式参数。

One example where nested functions are useful is to replace loops. The parameters to the nested function can act as induction variables which accumulate values. A simple example:

嵌套函数有用的一个例子是替换循环。嵌套函数的参数可以作为累积值的归纳变量。一个简单的例子:

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

In this case, it wouldn't really make sense to declare a function like facHelper globally, since users shouldn't have to worry about the p parameter.

在这种情况下,全局声明像facHelper这样的函数是没有意义的,因为用户不必担心p参数。

Be aware, however, that it can be difficult to test nested functions individually, since they cannot be referred to outside of their parent.

但请注意,单独测试嵌套函数可能很困难,因为它们不能在父项之外引用。

#2


3  

Functional programming isn't all-or-nothing. If nesting the functions makes more sense, I'd go with that approach. However, If you really want the internal functions to be purely functional, explicitly pass all the needed parameters into them.

功能编程不是全有或全无。如果嵌套函数更有意义,我会采用这种方法。但是,如果您确实希望内部函数纯粹是功能性的,请将所有必需的参数显式传递给它们。

Here's a little example in Scheme:

这是Scheme中的一个小例子:

(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))

Personally, I'd go with the first approach, but either will work equally well.

就个人而言,我会采用第一种方法,但两种方法都能同样有效。

#3


1  

Consider the following (contrived) Haskell snippet:

考虑以下(人为的)Haskell片段:

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

string is a locally bound named constant. But isn't it also a function taking no arguments that closes over lines and is therefore referentially intransparent? (In Haskell, constants and nullary functions are indeed indistinguishable!) Would you consider the above code “side-effecty” or non-functional because of this?

string是一个本地绑定的命名常量。但它是不是也没有一个函数不采用关闭线的参数,因此是不明确的不透明的? (在Haskell中,常量和nullary函数确实难以区分!)您是否会认为上述代码是“副作用”或因此无效?

#1


2  

Nesting functions is an excellent way to divide up the labor in many functions. It's not really "side-effecty"; if it helps, think of the captured variables as implicit parameters.

嵌套功能是在许多功能中分工的好方法。这不是真正的“副作用”;如果有帮助,可以将捕获的变量视为隐式参数。

One example where nested functions are useful is to replace loops. The parameters to the nested function can act as induction variables which accumulate values. A simple example:

嵌套函数有用的一个例子是替换循环。嵌套函数的参数可以作为累积值的归纳变量。一个简单的例子:

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

In this case, it wouldn't really make sense to declare a function like facHelper globally, since users shouldn't have to worry about the p parameter.

在这种情况下,全局声明像facHelper这样的函数是没有意义的,因为用户不必担心p参数。

Be aware, however, that it can be difficult to test nested functions individually, since they cannot be referred to outside of their parent.

但请注意,单独测试嵌套函数可能很困难,因为它们不能在父项之外引用。

#2


3  

Functional programming isn't all-or-nothing. If nesting the functions makes more sense, I'd go with that approach. However, If you really want the internal functions to be purely functional, explicitly pass all the needed parameters into them.

功能编程不是全有或全无。如果嵌套函数更有意义,我会采用这种方法。但是,如果您确实希望内部函数纯粹是功能性的,请将所有必需的参数显式传递给它们。

Here's a little example in Scheme:

这是Scheme中的一个小例子:

(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))

Personally, I'd go with the first approach, but either will work equally well.

就个人而言,我会采用第一种方法,但两种方法都能同样有效。

#3


1  

Consider the following (contrived) Haskell snippet:

考虑以下(人为的)Haskell片段:

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

string is a locally bound named constant. But isn't it also a function taking no arguments that closes over lines and is therefore referentially intransparent? (In Haskell, constants and nullary functions are indeed indistinguishable!) Would you consider the above code “side-effecty” or non-functional because of this?

string是一个本地绑定的命名常量。但它是不是也没有一个函数不采用关闭线的参数,因此是不明确的不透明的? (在Haskell中,常量和nullary函数确实难以区分!)您是否会认为上述代码是“副作用”或因此无效?