只编写静态方法相当于 C# 中的无副作用编程吗?
我有两个问题,源于观察到的 C# 静态方法的行为(我可能会误解):
第一: 在某种意义上,递归静态方法是否会通过静态方法在幕后实现的方式进行尾调用优化?
第二: 使用静态方法编写整个应用程序并且没有超出本地范围的变量,这是否等同于函数式编程?我很想知道,因为我仍然没有理解这个“无副作用”术语,我不断听到有关函数式编程的内容。
编辑: 让我提一下,我确实使用并理解为什么以及何时在正常的 C# OO 方法中使用静态方法,并且我确实理解尾调用优化不会显式地对递归静态方法进行。也就是说,我理解尾部调用优化是试图在每次传递时停止创建新的堆栈帧,并且我在几个点上观察到似乎是在其调用方法的框架内执行的静态方法,不过我可能误解了我的观察。
I have two questions, stemming from observed behavior of C# static methods (which I may be misinterpretting):
First:
Would a recursive static method be tail call optimized in a sense by the way the static method is implemented under the covers?
Second:
Would it be equivalent to functional programming to write an entire application with static methods and no variables beyond local scope? I am wondering because I still haven't wrapped my head around this "no side effects" term I keep hearing about functional programming..
Edit:
Let me mention, I do use and understand why and when to use static methods in the normal C# OO methodology, and I do understand tail call optimization will not be explicitly done to a recursive static method. That said, I understand tail call optimization to be an attempt at stopping the creation of a new stack frame with each pass, and I had at a couple points observed what appeared to be a static method executing within the frame of it's calling method, though I may have misinterpreted my observation.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
静态方法与尾递归优化无关。所有规则同样适用于实例和静态方法,但就我个人而言,我永远不会依赖 JIT 来优化我的尾部调用。此外, C# 编译器不会' t 发出尾部调用指令,但有时无论如何都会执行。简而言之,你永远不知道。
F# 编译器支持尾递归优化,并在可能的情况下将递归编译为循环。
请在此问题中查看有关 C# 与 F# 行为的更多详细信息。
既是否又是是。
从技术上讲,没有什么可以阻止您从静态方法(它本身就是静态方法!)调用Console.WriteLine,这显然有副作用。没有什么可以阻止您编写不更改任何状态(即实例方法不访问实例字段)的类(带有实例方法)。然而从设计的角度来看,这样的方法作为实例方法并没有真正的意义,对吧?
如果您
添加
一个项目到 .NET FrameworkList
(有副作用),您将修改其状态。如果您将项目
附加
到F# 列表,您将得到另一个列表,并且原始列表不会被修改。请注意,
append
确实是List
模块上的静态方法。 在单独的模块中编写“转换”方法鼓励无副作用设计,因为根据定义没有可用的内部存储,即使语言允许(F# 允许,LISP 不允许)。然而,没有什么能真正阻止您编写无副作用的非静态方法。最后,如果您想理解函数式语言概念,请使用一个! 编写操作不可变 F# 数据结构的 F# 模块比在带有或不带有静态方法的 C# 中模仿相同的模块要自然得多。
Static methods have nothing to do with tail recursion optimization. All the rules equally apply to instance and static methods, but personally I would never rely on JIT optimizing away my tail calls. Moreover, C# compiler doesn't emit tail call instruction but sometimes it is performed anyway. In short, you never know.
F# compiler supports tail recursion optimization and, when possible, compiles recursion to loops.
See more details on C# vs F# behavior in this question.
It's both no and yes.
Technically, nothing prevents you from calling
Console.WriteLine
from a static method (which is a static method itself!) which obviously has side-effects. Nothing also prevents you from writing a class (with instance methods) that does not change any state (i.e. instance methods don't access instance fields). However from the design point of view, such methods don't really make sense as instance methods, right?If you
Add
an item to .NET FrameworkList<T>
(which has side effects), you will modify its state.If you
append
an item to an F# list, you will get another list, and the original will not be modified.Note that
append
indeed is a static method onList
module. Writing “transformation” methods in separate modules encourages side-effect free design, as no internal storage is available by definition, even if the language allows it (F# does, LISP doesn't). However nothing really prevents you from writing a side-effect free non-static method.Finally, if you want to grok functional language concepts, use one! It's so much more natural to write F# modules that operate immutable F# data structures than imitate the same in C# with or without static methods.
CLR 确实做了一些尾部调用优化,但仅限于 64 位 CLR 进程。请参阅以下内容了解完成位置:David Broman 的 CLR 分析 API 博客:尾部调用 JIT 条件。
至于仅使用静态变量和局部作用域构建软件,我已经做了很多次,而且实际上很好。它只是另一种与 OO 一样有效的做事方式。事实上,因为函数/闭包之外没有状态,所以更安全、更容易测试。
不过,我首先从头到尾读完了整本 SICP 书: http://mitpress.mit.edu/sicp/
无副作用仅仅意味着该函数可以使用相同的参数调用任意多次,并且始终返回相同的值。这只是定义了函数的结果始终一致,因此不依赖于任何外部状态。因此,并行化函数、缓存它、测试它、修改它、装饰它等都很简单。
但是,没有副作用的系统通常是无用的,因此执行 IO 的事情总是会产生副作用。它允许您整齐地封装其他所有内容,但这才是重点。
不管人们怎么说,对象并不总是最好的方式。事实上,如果您曾经使用过 LISP 变体,您无疑会确定典型的 OO 有时确实会造成阻碍。
The CLR does do some tail call optimisations but only in 64-bit CLR processes. See the following for where it is done: David Broman's CLR Profiling API Blog: Tail call JIT conditions.
As for building software with just static variables and local scope, I've done this a lot and it's actually fine. It's just another way of doing things that is as valid as OO is. In fact because there is no state outside the function/closure, it's safer and easier to test.
I read the entire SICP book from cover to cover first however: http://mitpress.mit.edu/sicp/
No side effects simply means that the function can be called with the same arguments as many times as you like and always return the same value. That simply defines that the result of the function is always consistent therefore does not depend on any external state. Due to this, it's trivial to parallelize the function, cache it, test it, modify it, decorate it etc.
However, a system without side effects is typically useless, so things that do IO will always have side effects. It allows you to neatly encapsulate everything else though which is the point.
Objects are not always the best way, despite what people say. In fact, if you've ever used a LISP variant, you will no doubt determine that typical OO does sometimes get in the way.
关于这个主题有一本非常好的书, http://www.amazon.com/真实世界功能编程示例/dp/1933988924。
不幸的是,在现实世界中,由于团队技能或现有代码库的原因,使用 F# 并不是一种选择,这是我喜欢这本书的另一个原因,因为它展示了在您日常使用的代码中实现 F# 功能的多种方法。对我来说,至少状态错误的大量减少是值得稍微减少 OOP 正统观念的,因为状态错误的调试时间比简单的逻辑错误要长得多。
在大多数情况下,没有静态状态并且仅对给定的参数在静态方法中进行操作将消除副作用,因为您将自己限制为纯函数。不过,需要注意的一点是在这样的函数中检索要操作的数据或将数据保存到数据库。不过,通过将静态方法委托给较低级别的对象命令来操作状态,结合 OOP 和静态方法可以在这方面有所帮助。
尽可能保持对象不可变也是强制函数纯度的一个很大的帮助。任何操作的对象都应该返回一个新的修改实例,并丢弃原始副本。
There's a pretty good book written on this subject, http://www.amazon.com/Real-World-Functional-Programming-Examples/dp/1933988924.
And in the real world using F# unfortunately isn't an option due to team skills or existing codebases, which is another reason I do love this book, as it has shows many ways to implement F# features in the code you use day to day. And to me at least the vast reduction in state bugs, which take far longer to debug than simple logic errors, is worth the slight reduction in OOP orthodoxy.
For the most part having no static state and operating in a static method only on the parameters given will eliminate side-effects, as you're limiting yourself to pure functions. One point to watch out for though is retrieving data to be acted on or saving data to a database in such a function. Combining OOP and static methods, though, can help here, by having your static methods delegate to lower level objects commands to manipulate state.
Also a great help in enforcing function purity is to keep objects immutable whenever possible. Any object acted on should return a new modified instance, and the original copy discarded.
关于第二个问题:我相信你的意思是可变数据结构的“副作用”,显然这对于(我相信)大多数函数式语言来说不是问题。例如,Haskel 大部分(甚至全部!?)使用不可变的数据结构。所以不存在“静态”行为。
Regarding second question: I believe you mean "side effects" of mutable data structures, and obviously this is not a problem for (I believe) most functional languages. For instance, Haskel mostly (or even all!?) uses immutable data structures. So there is nothing about "static" behaviour.