为什么Lispers会像样本1中所示那样格式化代码,而不是样本2中所示?对我来说(我想,对于大多数来自不同编程背景而不是Lisp的人),样本2中显示的格式将更容易阅读。 Lispers是否更喜欢样本1样式?
(defun factorial (n)
(if (<= n 1)
1
(* n (factorial (- n 1)))))
(defun factorial (n)
(if (<= n 1)
1
(* n (factorial (- n 1)))
)
)
答案 0 :(得分:33)
LISP IDE环境倾向于自动平衡括号并根据嵌套级别管理缩进。样本2在这些情况下没有任何优势。
在C / FORTRAN / Pascal遗产中,您倾向于强调对嵌套的排序(代码解析树更浅更宽)。范围的结束是您的代码中更重要的事件:因此重点已经并且在某种程度上仍然更为重要。
答案 1 :(得分:21)
对于有经验的Lisp用户,嵌套级别比查找右括号更重要。将右括号放在自己的行上对嵌套级别没有任何帮助。
基本思想是括号直接围绕其内容。
(a)
而不是
(a
)
以下是:
(defun foo (bar)
(foo (bar (baz
...
)
...
)
...
)
)
VS。
(defun foo (bar)
(foo (bar (baz ...) ...) ...))
编辑Lisp文本时的一个基本思想是,您可以通过(双击)括号来选择列表(或者当光标位于表达式内或括号上时使用键命令)。然后,您可以剪切/复制表达式并将其粘贴到其他函数中的另一个位置。下一步是选择其他功能和 重新缩进功能。完成。没有必要删除或引入用于结束括号的新行。只需粘贴并重新缩进。它只是适合。否则你会浪费时间格式化代码,或者你需要用一些编辑器工具重新格式化代码(以便关闭括号是在他们自己的行上。大多数时候它会创建额外的工作并阻碍移动代码。
有一次有经验的Lispers有时会在自己的行上写下括号:
(defvar *persons*
(list (make-person "fred")
(make-person "jane")
(make-person "susan")
))
这表示可以添加新人。将光标直接放在最后一行的第二个右括号之前,按c-o(空行),添加该子句并缩进它们再次对齐的括号。这样可以省去找到正确括号的“麻烦”,然后在所有括号在一行上关闭时按回车键。
答案 2 :(得分:15)
因为没有必要排队关闭parens。它不会在语义上添加任何内容。要知道要关闭多少,大多数Lispers使用一个好的编辑器,例如Emacs,它将parens与关闭parens相匹配,从而使任务变得微不足道。
在Python中,根本没有关闭的parens或end
关键字,Pythonistas就可以了。
答案 3 :(得分:9)
在使用Lisp一段时间之后,你不再注意到括号了,所以你的例子中有两个因为在它的末尾有一堆不必要的空格。更具体地说,大多数lispers使用一个知道括号的编辑器并负责正确关闭表单,因此开发人员不需要像在例如中那样匹配开括号和右括号。下进行。
至于最后一个表格是否应
(* n (factorial (- n 1)))
或
(* n
(factorial (- n 1)))
主要归结为个人偏好以及代码中发生了多少事情(在这种情况下,我更喜欢前者,因为代码中发生的事情很少)。
答案 4 :(得分:5)
除了大多数Lisp IDE使用paren匹配这一事实外,你用来编写任何合理程序的空间量都是荒谬的!你会得到所有滚动的腕管。如果将所有右括号放在自己的行上,那么1,000行程序将接近一百万行代码。它可能看起来更漂亮,更容易在一个小程序中阅读,但这个想法根本无法很好地扩展。
答案 5 :(得分:5)
为什么C程序员写的东西如下:
((a && b) || (c && d))
而不是
((a && b) || (c && d
)
)
不会#2更容易阅读吗? :)
但是,严肃地说,成熟的结束风格也适用于其他语言。
#include <stdlib.h>
#include <stdio.h>
int main(void)
{ int i, j, k;
for (i = 0; i < 1000; i++)
{ for (j = 0; j < 1000; j++)
{ for (k = 0; k < 1000; k++)
{ if (i * i + j * j == k * k)
{ printf("%d, %d, %d\n", i, j, k); } } } }
return 0; }
过了一会儿,你不会看到&#34;&#34;大括号,只是缩进给出的结构。 if / else看起来像这样:
if (cond)
{ stmt;
stmt; }
else
{ stmt; }
开关:
switch (expr)
{ case '3':
stmt;
break;
case '4':
{ stmt;
break; } }
结构:
struct foo
{ int x;
struct bar
{ int y; };
union u
{ int a, b;
char c; }; };
答案 6 :(得分:4)
我在PHP中使用CakePHP框架及其许多嵌套array()
结构遇到了同样的困境,有时甚至超过5层。过了一会儿,我意识到关于右括号的OCD什么也没做,只是浪费了我宝贵的开发时间,让我分心了最终的目标。缩进是格式化中最有用的方面。
答案 7 :(得分:3)
保存所有空间似乎是主要原因。如果它几乎可读(特别是一旦你习惯了语法)为什么要使用额外的3行。
答案 8 :(得分:3)
第一个也是显而易见的原因:第一个案例中有4个LOC,第二个案例中有7个LOC。
任何了解LISP语法的文本编辑器都会突出显示匹配/不匹配的括号,因此不是问题所在。
答案 9 :(得分:2)
因为Lisp程序员会查看缩进和“形状”,而不是括号。
答案 10 :(得分:1)
您可能会考虑没有()的Lisp变体。在Genyris看起来像这样:
def factorial (n)
if (< n 2) 1
* n
factorial (- n 1)
答案 11 :(得分:1)
似乎这不是一个真正的问题,只是对Lisp的判断,但答案非常明显(但显然不是非Lispers!):Lisp中的括号绝不等同于C-中的括号和语言一样 - 相反,它们恰好等同于C语言中的括号。 Lispers通常不会写
(foo bar baz
)
出于完全相同的原因,C程序员通常不会写
foo(bar, baz
);
原因
(if foo
(bar)
(baz))
与
不同if (foo) {
bar();
} else {
baz();
}
与if
有关,而不是括号!
答案 12 :(得分:0)
我有一个解释,关于C程序员有问题,Lispers将所有剩下的结束括号放在最后。在我看来,这种做法可能是括号的含义。这个解释重叠并扩展了其他一些答案。
对于C程序员(或其他使用{braces}的语言,如C),Lisp看起来像是使用#|评论|#而不是/ *评论* /。看起来像Lisp括号()代替C括号{}。
但实际上,Lisp括号与C(和使用括号的类似语言)非常接近。考虑
defun f () nil
这定义了一个绝对没有的函数f,nil。我完全按照Lisp Listener的那样输入该行,它只是响应&#34; F&#34;。换句话说,它接受它,没有围绕整个事物的开始和结束括号。
当您在Listener中键入它时,我认为会发生什么?Listener会将您键入的内容传递给Eval。将其传递给Eval可以表示为:
Eval( Listener() )
Listener接受我的输入并返回我输入的内容。你会考虑&#34; Listener()&#34;在表达式中替换,使其变为
Eval (defun f () nil)
REPL(read,eval,print,loop)可以在概念上表示为递归
/* REPL expressed in C */
Loop()
{
Print ( Eval ( Listen () ) );
Loop();
}
在示例中理解的是,Listen(),Eval()和Print()在别处定义或内置于语言中。
听众让我输入
setf a 5
还允许我输入
(setf a 5)
但是当我输入
时会抱怨((setf a 5))
如果括号等同于C语言括号,则侦听器会接受它。在我的C \ C ++编译器中,我可以输入
void a_fun(void)
{{
}}
没有抱怨。我甚至可以输入
void a_fun(void)
{{{
}}}
没有来自C / C ++编译器的抱怨。
但如果我敢打字
void a_fun((void))
{
}
我的C / C ++编译器抱怨 - 就像Lisp监听器抱怨一样!
对于编写嵌套函数调用的C程序员来说,编写
似乎更自然fun_d( fun_c( fun_b( fun_a())));
而不是写
fun_d( fun_c( fun_b( fun_a()
)
)
);
在C中,大括号划分了一段代码。代码块是函数定义的一部分。 Lisp括号不会划分块。它们有点像C&C的括号。在C中,括号标出一个列表;也许是传递给函数的参数列表。
在Lisp中,似乎是开头的括号&#34;(&#34;意味着我们将开始一个新的列表,CONS构造的CAR侧将指向该列表。然后我们列出要包含的项目或可选择CAR的CAR侧指向,CDR侧指向nil(列表末尾)或下一个CONS。右括号&#34;)&#34;表示以nil终止列表,然后返回继续上一级列表。所以,C语言不做CONS。所以,C&C的括号和Lisp之间有一些区别。即便如此,似乎非常接近,甚至可能实际上是相同的事情。
有些人写道,如果你将函数移到括号之外,那么Lisp会变得更像C语言。例如,
(setf a 5)
变为
setf(a 5)
但实际上,Eval要求列表中的第一项是Eval需要调用的函数。它更像是将C函数传递给函数的指针。那么,它到底是什么
eval(setf a 5)
看起来更像是C.你甚至可以在听众中输入它,而不会受到听众或评论的抱怨。你只是说eval应该调用eval,然后调用setf。列表的其余部分是setf的参数。
无论如何,这就是我认为我所看到的。
只是Eval需要知道要调用哪个处理器。
我认为这个解释提供了一个理解为什么C程序员最初认为Lispers应该在他们关闭的开括号下对齐括号。 C程序员错误地认为Lisp的括号对应于C&C的大括号。他们没有。 Lisp的括号对应于C&C的括号。