基于块的语言与基于文本的语言在许多方面有所不同,这主要是因为它们专为新手用户而设计。以下是设计自己的基于块的语言时需要考虑的事项。
使用从 1 开始编号的列表
新手程序员第一次遇到从零开始编号的列表时,会感到非常不适应。因此,Blockly 遵循 Lua 和 Lambda Moo 的做法,将列表和字符串编号从 1 开始。
为了更高级地使用 Blockly,我们支持从零开始的列表,以便更轻松地转换为文本。对于年龄较小或更为新手的观众,我们仍建议采用从 1 开始编号的编号方式。
建议:1 是第一个数字。
支持宽松的命名规则
新手程序员不会认为 location_X
和 location_x
是不同的变量。因此,Blockly 遵循 BASIC 和 HTML 的做法,使变量和函数不区分大小写。Scratch 使用更细微的方法(如右侧所示),并且对变量名称区分大小写,但对等式检查不区分大小写。
此外,Blockly 不要求变量和函数符合典型的 [_A-Za-z][_A-Za-z0-9]*
架构。如果想将变量命名为 List
of zip codes
或 רשימת מיקודים
,那完全没问题。
建议:忽略大小写,允许使用任何名称。
将所有变量设为全局变量
新手程序员也难以理解作用域。因此,Blockly 遵循 Scratch 的做法,将所有变量都设为全局变量。全局变量的唯一缺点是递归更为棘手(必须将变量推送和弹出到列表中),但这是一种超出 Blockly 目标用户范围的编程技术。
建议:范围超出范围,请稍后再处理。
考虑如何处理可选返回值
基于文本的编程中的许多函数都会执行操作,然后返回一个值。此返回值可以使用,也可以不使用。例如,堆栈的 pop()
函数。您可以调用 pop 来获取并移除最后一个元素,也可以调用 pop 来仅移除最后一个元素,并忽略返回值。
var last = stack.pop(); // Get and remove last element.
stack.pop(); // Just remove last element.
基于块的语言通常不擅长忽略返回值。值块必须插入接受该值的某个对象。有几种方法可以解决此问题。
a) 绕开问题。大多数基于块的语言在设计时都会避免出现这些情况。例如,Scratch 中没有同时具有副作用和返回值的任何代码块。
b) 提供两个代码块。如果工具箱中的空间不是问题,一个简单的解决方案是,为每种类型的代码块提供两个,一个带有返回值,一个不带返回值。缺点是,这可能会导致工具箱中包含许多几乎完全相同的块,让人感到困惑。
c) 修改一个分块。使用下拉菜单、复选框或其他控件,让用户选择是否有返回值。然后,该块会根据其选项更改形状。您可在 Blockly 的列表访问块中查看示例。
d) 使用该值。第一个版本的 App Inventor 创建了一个特殊的管道块,用于吞噬任何关联的值。用户不理解该概念,因此 App Inventor 的第二个版本移除了管道块,而是建议用户将值直接赋给一个一次性变量。
建议:每种策略都有优缺点,请选择最适合您的用户的策略。
生成可读代码
高级 Blockly 用户应该能够查看生成的代码(JavaScript、Python、PHP、Lua、Dart 等),并立即识别出自己编写的程序。这意味着,需要付出额外的努力来确保此机器生成的代码可读。多余的圆括号、数字变量、压缩的空格和冗长的代码模板都会妨碍生成优雅的代码。生成的代码应包含注释,并且应符合 Google 的样式指南。
建议:为您生成的代码感到自豪。向用户显示。
接受不同语言之间的差异
追求简洁代码的一个副作用是,Blockly 的行为在很大程度上取决于交叉编译语言的行为方式。最常见的输出语言是 JavaScript,但如果 Blockly 要交叉编译为其他语言,则不应尝试在两种语言中保留完全相同的行为。例如,在 JavaScript 中,空字符串为 false,而在 Lua 中,空字符串为 true。无论目标语言如何,为 Blockly 的代码定义单一行为模式都会导致不可维护的代码,看起来像是从 GWT 编译器中生成的。
建议:Blockly 不是语言,请允许现有语言影响行为。