一种基于限制传递给函数的参数数量的语言


16

这个想法的灵感来自事实运算符,例如+,-,%等,可以看作是传递了一个或两个参数且没有副作用的函数。假设我或其他人编写了一种语言,该语言可以阻止传递两个以上的参数,并且也只能通过返回值来工作:

a)这样的语言会导致更容易理解的代码吗?

b)代码流程会更清晰吗?(被强制执行更多的步骤,可能会减少“隐藏”的互动

c)限制是否会使该语言在更复杂的程序中显得过于庞大。

d)(奖励)对优点/缺点的任何其他评论

注意:

仍然必须做出两个决定-第一个是是否允许用户在main()或其等效外部进行输入,以及关于传递数组/结构时将发生什么的规则。例如,如果某人希望单个函数添加多个值,则可以通过将其捆绑到数组中来解决该限制。可以通过不允许数组或结构与其自身进行交互来阻止这种情况,例如,仍然可以使您根据位置将每个数字除以不同的数量。


4
你好 利弊清单往往会给出错误的答案。有什么方法可以改写您的问题,以便仍然以其他格式获取您所需的信息?
MetaFight

22
你的推理对我什至没有意义。有些函数的参数很少,所以我们限制所有函数吗?通常,当有人提出任意限制时,就有理由要有所收获。我看不出这有什么好处。

2
并不是说“如果...”问题有天生的错误(尽管有时很难回答@MetaFight所说的),但是即使是您,如果您想到了这个问题并且很认真地问了一个问题,也不能真正地命名一个受益,那么我可以肯定我对“什么?不!那是愚蠢的,为什么要这么做”的最初反应是正确的。

6
有相当多的语言每个函数只允许一个参数:任何基于lambda演算的语言。结果通常是使用单个列表参数的函数,或者返回使用下一个参数直到所有参数都已处理的函数:result = f(a)(b)…(z)。ML语言家族(例如Haskell)就是这种情况,但从概念上讲,其他语言(例如Lisp,JavaScript或Perl)也是如此。
阿蒙(Amon)

3
@Orangesandlemons:好的,那么我可以使用乘法和加法(用于编码)以及除法和减法(用于解码)对单个整数内的任意整数进行编码。因此,您还需要禁止整数,或者至少禁止乘法,加法,除法和减法。(编程功能的一个结果是,您几乎可以使用几乎任何东西对几乎任何东西进行编码,因此对事物的限制确实非常非常困难。通常,限制实际上并没有“限制”任何东西,它们只是使程序员烦恼。)
Jörg W Mittag

Answers:


40

罗伯特·C·马丁(Robert C. Martin)在他的《清洁代码》一书中建议大量使用最多包含0、1或2个参数的函数,因此至少有一位经验丰富的书作者认为使用这种样式可使代码变得更简洁(但是,他当然不是这里的最终权威,他的观点尚有争议)。

鲍勃·马丁(Bob Martin)正确的恕我直言的地方是:具有3个或更多参数的函数通常是代码气味的指示器。在很多情况下,参数可能会组合在一起以形成组合的数据类型,在其他情况下,它可能只是简单地执行过多操作的指标。

但是,我认为为此发明一种新的语言不是一个好主意:

  • 如果您真的想在整个代码中强制执行这样的规则,则只需要一种用于现有语言的代码分析工具,而无需为此发明一种全新的语言(例如,对于C#,可能会使用“ fxcop”之类的东西) )。

  • 有时,将参数组合为新类型似乎并不值得,也可以成为纯粹的人为组合。例如,请参见.Net框架中的File.Open方法。它需要四个参数,我很确定该API的设计者是故意这样做的,因为他们认为这是向函数提供不同参数的最实用方法。

  • 有时在现实世界中,由于技术原因,有两个以上的参数使事情变得更简单(例如,当您需要将1:1映射到现有API时,您必须绑定简单数据类型的使用,并且不能合并不同的数据类型参数放入一个自定义对象)


16
具有多个参数的气味通常是不同的参数实际上属于同一类。以体重指数(BMI)的计算为例。它是一个人的身高和体重的函数。f(length,weight),但那两个参数确实属于同一参数,因为您会用一个人的身高和另一个人的体重进行此计算吗?因此,为了更好地表示,您将得到f(person),其中person可以具有重量,长度接口。
Pieter B

@PieterB:当然,请看我的编辑。
布朗

5
小而细小的语言“ nitpick”:“可能表明代码有异味”定义上的代码异味是否仅表示您应该重新考虑某些内容,即使您最终不更改代码也是如此?因此,不会“指示”代码气味。如果特定方面表明有问题的可能性,则代码异味。没有?
jpmc26 2016年

6
@ jpmc26:从另一个角度来看,代码气味是一个可能的问题,而不是一个提示;-)这完全取决于代码气味的确切定义(并且一旦闻到,它就变坏了,不是吗? ?)
hoffmale

3
@PieterB有人真的这样做吗?现在,每次您只想使用两个任意值来计算BMI时,就必须创建一个新的Person实例。当然,如果您的应用程序已经开始使用Persons,并且发现自己经常做类似f(person.length,person.height)的操作,则可以对其进行一些清理,但是专门针对组参数创建新对象似乎有些过头。
Lawyerson

47

已经有许多种语言已经可以通过这种方式工作,例如Haskell。在Haskell中,每个函数仅使用一个参数,并返回一个值。

始终可以将接受n个参数的函数替换为接受n-1个参数并返回接受最终参数的函数。递归地应用它,总是有可能用一个只接受一个参数的函数替换一个接受任意数量参数的函数。并且该变换可以通过算法机械地执行。

在1950年代Haskell Curry对其进行了广泛研究之后,MosesSchönfinkel(于1924年对其进行了描述)和Gottlob Frege(于1893年将其预示)之后,这被称为Frege-Schönfinkeling,Schönfinkeling,Schönfinkel-Currying或Currying。

换句话说,限制参数的数量将产生零影响。


2
当然,这就是如果您允许返回函数。即使没有,您所要做的就是在主程序中调用下一个函数。也就是说,您可能有1 + 1 + 1,其中第一个加法是一个函数,该函数返回一个用于附加加法的函数,或者可以简单地调用两次。从理论上讲,后一种样式更清洁,还是我弄错了?

5
“几乎完全零影响”-OP的问题是代码的可读性是否会增加或减少,我想您不会声称对语言的这种设计决定不会对此产生影响,对吗?
布朗

3
@DocBrown具有不错的功能和运算符的重载,我可以使用一种咖喱语言,并使它看起来像是一种非咖喱语言。示例:f *(call_with: a,b,c,d,e) 重载call_with :开始一个链,,扩展该链,并*在LHS上f通过一次传递链的每个内容来调用。一个足够弱的运算符重载系统只会使语法变得繁琐,但这是运算符重载系统的主要缺陷。
Yakk 2016年

实际上,Haskell的currying将具有n个参数的函数简化为具有一个参数的函数返回具有n-1个参数的另一个函数。
Ryan Reich

2
@RyanReich是正确的,您可以在其类型签名中看到Haskell函数的“参数数量”的“重影”。这是一个鬼影,而不是真正的签名,因为通常您无法知道签名中的最后一个类型变量是否也是函数类型。无论如何,存在于此的鬼影不会使以下事实无效:在Haskell中,所有函数都带有1个参数,并返回一个非函数值或另一个也带有1个参数的函数。这内置于->的关联性中:a-> b-> c是a->(b-> c)。因此,您在这里大都是错的。
2013年

7

最近几周我一直在花一些时间尝试学习J计算机语言。在J中,几乎所有东西都是一个运算符,因此您只会得到“ monads”(仅具有一个参数的函数)和“ dyads”(具有两个自变量的函数)。如果需要更多参数,则必须以数组形式提供它们,或以“框”形式提供它们。

J可能非常简洁,但是像其前身APL一样,它也可能非常隐秘-但这主要是由于创建者的目标是模仿数学简洁性。通过使用名称而不是字符来创建运算符,可以使J程序更具可读性。


嗯,所以它允许数组与自己交互。

5

基于其如何约束开发人员的语言取决于以下假设:该语言开发人员对每个程序员的需求的理解要好于程序员对自己的需求的理解。在某些情况下,这实际上是有效的。例如,许多人认为对需要使用互斥量和信号量进行同步的多线程编程的约束是“好的”,因为大多数程序员完全不知道这些约束对它们隐藏的底层特定于机器的复杂性。同样,很少有人希望完全掌握多线程垃圾回收算法的细微差别。优先使用一种不会让您破坏GC算法的语言,而不是一种迫使程序员意识到太多细微差别的语言。

您必须提出一个有效的论据,说明为什么作为语言开发人员,您比使用您的语言的程序员理解传递的论据要好得多,以至于有防止他们从事有害的事情的价值。我认为这将是一个艰难的论点。

您还必须知道程序员克服您的约束。如果他们需要3个或更多参数,则将使用currying之类的技术将其转换为较少参数的调用。但是,这通常是以可读性为代价的,而不是提高可读性。

我所知道的大多数使用这种规则的语言都是esolangs,这些语言旨在证明您确实可以使用有限的功能集进行操作。特别是,每个字符都是操作码的esolang倾向于限制参数的数量,这仅仅是因为它们需要使操作码列表简短。


这是最好的答案。
贾里德·史密斯

1

您将需要两件事:

  • 关闭
  • 复合数据类型

我将添加一个数学示例来解释JörgW Mittag编写的答案

考虑高斯函数

高斯函数的形状有两个参数,即平均值(曲线的中心位置)和方差(与曲线的脉冲宽度有关)。除了两个参数外,还需要提供free变量的值x以便对其进行评估。

在第一步中,我们将设计一个采用所有三个参数的高斯函数,即均值,方差和自由变量。

在第二步中,我们创建一个复合数据类型,将平均值和方差组合为一件事。

在第三步中,我们通过创建绑定到在第二步中创建的复合数据类型的高斯函数的闭包来创建高斯函数的参数化。

最后,我们通过将free变量的值传递x给它来评估在第三步中创建的闭包。

因此,结构为:

  • 评估(计算)
    • ParameterizedGaussian(闭包:公式,加上一些绑定变量)
      • GaussianParameters(复合数据类型)
        • 平均值)
        • 方差(值)
    • X(自由变量的值)

1
  1. 在几乎任何编程语言中,您都可以将列表,数组,元组,记录或对象的某些类型作为唯一的参数传递。唯一的目的是保留其他项目,而不是将它们分别传递给函数。某些Java IDE甚至具有“ 提取参数对象 ”功能来执行此操作。在内部,Java通过创建和传递数组来实现可变数量的参数。

  2. 如果您真的想以最纯净的形式进行操作,则需要查看lambda演算。正是您所描述的。您可以在网上搜索它,但是对我来说有意义的描述是在Types and Programming Languages中

  3. 查看HaskellML编程语言(ML更简单)。它们都基于lambda演算,并且从概念上讲,每个函数只有一个参数(如果稍微斜视一下)。

  4. 乔什·布洛赫(Josh Bloch)的第2项是:“面对许多构造函数参数时,请考虑构造器。” 您可以看到它的详细程度,但是使用以这种方式编写的API可以很高兴。

  5. 一些语言已命名参数,这是使庞大的方法签名更易于浏览的另一种方法。 例如,Kotlin已经命名了参数

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.