为什么要区分返回值的方法和不返回值的方法?
为什么某些语言会区分返回值的方法和不返回值的方法?
即在 Oracle 的 PL/SQL 中,函数和过程之间的主要区别是函数必须返回值,而过程则不能。
同样,对于不支持的语言,为什么不呢?
编辑:我发现一个相关问题可能会让阅读此问题的人感兴趣:
Why do some languages differentiate between methods that return a value and methods that don't?
i.e. in Oracle's PL/SQL, where the primary difference between a function and a procedure is that the function must return a value, and the procedure must not.
Likewise for languages that don't, why not?
EDIT: I have found a related question that might interest people reading this question:
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
因为在计算机科学理论和实践的原始概念中,函数和子例程实际上彼此无关。
FORTRAN 通常被认为是第一个实现这两者并展示其区别的语言。 (早期的 LISP 在这方面也起到了一定程度的相反作用,但它在学术界之外几乎没有影响)。
遵循数学传统(CS 在 60 年代仍然是数学传统的一部分),函数仅被视为参数化数学计算的封装,其目的只是将值返回到更大的表达式中。 您可以将其称为“裸”(F = AZIMUTH(SECONDS)),这只是一个微不足道的用例。
另一方面,子例程被视为一种命名一组旨在产生某种效果的语句的方式。 参数极大地提高了它们的可用性,并且允许它们返回修改后的参数值的唯一原因是它们可以报告其状态而不必依赖全局变量。
所以,除了封装和参数之外,它们实际上没有任何概念上的联系。
真正的问题是:“为什么这么多开发人员认为它们是一样的?”
答案是 C。
当 K+R 最初为 PDP-11 设计高级宏汇编类型语言时(可能是从 PDP-8 开始的?),他们并没有硬件独立性的幻想。 事实上,该语言的每个“独特”功能都是 PDP 机器语言和体系结构的反映(参见 i++ 和 --i)。 其中之一是函数和子例程在 PDP 中的实现是相同的,只是调用者只是忽略了子例程的返回值(在 R0 [, R1] 中)。
因此,void 指针诞生了,在 C 语言接管整个编程世界之后,随之而来的是一种误解,即这种 HW/OS 实现工件(尽管在几乎所有后续平台上都是如此)与语言语义相同。
Because in the original conceptions of Computer Science theory and practice, Functions and Subroutines had virtually nothing to do with each other.
FORTRAN is usually credited as the first language that implemented both of these and demonstrated the distinctions. (Early LISP had a somewhat opposing role in this also, but it had little impact outside of academia).
Following from the traditions of mathematics (which CS was still part of in the 60's) functions were only seen as the encapsulation of parametrized mathematical calculations solely intended to return a value into a larger expression. That you could call it "bare" (F = AZIMUTH(SECONDS)) was merely a trivial use case.
Subroutines, on the other hand were seen as a way to name a group of statements meant to have some effect. Parameters were a huge boost to their usability and the only reason that they were allowed to return modified parameter values was so that they could report their status without having to rely on global variables.
So, they really had no conceptual connection, other than encapsulation and parameters.
The real question, is: "How did so many developers come to see them as the same?"
And the answer to that is C.
When K+R originally designed their high-level macro assembler type language for the PDP-11 (may have started on the PDP-8?), they had no delusions of hardware independence. Virtually every "unique" feature of the language was a reflection of the PDP machine language and architecture (see i++ and --i). One of these was the realization that functions and subroutines were implemented identically in the PDP except that the caller just ignored the return value (in R0 [, R1]) for subroutines.
Thus was born the void pointer, and after the C language had taken over the whole world of programming, with it came the misperception that this HW/OS implementation artifact (though true on almost every subsequent platform) was the same as the language semantics.
在纯或效果类型的设置中,存在天壤之别,因为显然“不返回任何内容”的方法仅对其副作用有用。
这类似于表达式和语句之间的区别,它可以整理语言并消除一类经常出错的程序(当然,这就是 C 不这样做的原因;))。
举一个小例子,当您清楚地区分表达式和语句时,与
if(x == 3) 相对的
if(x = 3)
在语法上是不正确的 ( (在需要表达式的地方使用语句)而不仅仅是类型错误(在需要布尔值的地方使用整数)。 这样做的好处是还不允许使用if(x = true)
,而在赋值是具有其右操作数值的表达式的上下文中,基于类型的规则允许使用 if(x = true)。在用 monad 封装效果的语言中,重要的区别在于:
()
的函数是纯函数,只能返回一个无用的空值,称为()
发散函数,这些函数除了 IO(或任何)monad 中的效果外没有“结果”In a pure or effect-typed setting there is a world of difference, because obviously methods that "don't return anything" are only useful for their side effects.
This is analogous to the distinction between expressions and statements, which can declutter a language and eliminate a class of usually-mistaken programs (which, of course, is why C doesn't do it ;)).
To give one tiny example, when you distinguish clearly between expressions and statements,
if(x = 3)
, as opposed toif(x == 3)
is syntactically incorrect (for using a statement where an expression was expected) and not merely a type error (for using an integer where a boolean was expected). This has the benefit of also disallowingif(x = true)
which would be permitted by a type-based rule in a context where assignments are expressions which have the value of their right operand.In a language which encapsulates effects with monads, the important distinction becomes the one between:
()
which are pure functions and can only return one useless empty value called()
or divergeIO ()
(or unit in some other monad) which are functions with no "result" except effects in the IO (or whichever) monad请原谅我回答一个两年前的问题,尤其是我自己的语言 Felix http://felix-lang.org 但无论如何:)
在 Felix 中,函数和过程是根本不同的,不仅仅是过程有副作用并在语句中调用,而函数没有副作用并在表达式中使用(因为Felix 也有生成器,它们是带有副作用的函数.. :)
不,执行模型根本不同,主要是出于性能原因,但并非完全不同。 该模型是:
这通常是低效的,那么为什么要这样做呢? 答案是:Felix 过程都是潜在的协同例程(纤维)。 他们可以通过访问通道将控制权切换到另一个过程。 这会导致控制权的交换。
操作系统通常将堆栈指针交换为线程,这相当快,但在线性地址机器上有一个基本问题:您要么必须将堆栈的最大大小限制为一个可笑的小值,要么将线程数限制为一个可笑的小值。小值。 在 32 位机器上,没有足够的地址空间来考虑这个解决方案。 在 64 位机器上,堆栈交换具有更大的潜力,但是当然,用户需求总是会在硬件发布 3 天后增长到超过硬件.. :)
Felix 只是将单个指针交换到基于堆的堆栈,因此上下文交换机的速度快得令人眼花缭乱,而且浪费的地址空间很少。当然,成本是过程调用的堆分配。
在编译器中,理论模型的很多架构都是在“假设”的基础上进行优化的,因此实际性能和实现可能与理论模型有很大不同,前提是编译器可以证明你无法分辨区别.. 除了被剥夺了悠闲地泡一杯咖啡的机会之外:)
因此,在这里,对于为什么函数和过程可能会受到不同的对待,您有不同的答案。
Excuse me answering a two year old question, especially with something unique to my own language Felix http://felix-lang.org but here goes anyhow :)
In Felix, functions and procedures are fundamental different, and it isn't just that procedures have side effects and are called in statements, whereas functions don't have side effects and are used in expressions (because Felix also has generators which are functions with side-effects .. :)
No, the execution model is fundamentally different, primarily for performance reasons, but not entirely. The model is:
This is typically inefficient, so why do it? The answer is: Felix procedure are all potentially co-routines (fibres). They can switch control to another procedure by accessing a channel. This causes an exchange of control.
The OS typically swaps stack pointers for threads, which is reasonably fast, but has a fundamental problem on linear address machines: you either have to limit the maximum size of the stack to a ridiculously small value, or limit the number of threads to a ridiculously small value. On a 32 bit machine, there is not enough address space to even contemplate this solution. On a 64 bit machine, stack swapping has more potential, but of course user demands always grow to outstrip hardware 3 days after it is released .. :)
Felix just swaps a single pointer to the heap-based stacks, so context switches are blindingly fast and very little address space is wasted. Of course the cost is heap allocations on procedure calls.
In the compiler, a lot of the architecture of the theoretical model is optimised away on an "as-if" basis, so actual performance and implementation can be quite different to the theoretical model, provided the compiler can prove that you can't tell the difference .. other than being denied the opportunity to make a cup of coffee with leisure :)
So here, you have a different answer as to why functions and procedures might be treated differently.