如果您正在学习一门语言,您可能听说过“语法”这个词并且一直在处理它。 (该死的语法错误)。
几天前的晚上,我在心里想,我从来没有认真遵循过编程范式和技术,今天我开始从我经常听到的最小主题中学习(即使我已经记住了)。我创建了这个存储库。我采取的学习路径很有特色(在 learning_list.md 文件中)。让我们来说说,与我们用来交流思想和感情的自然语言相比,编程语言可以被视为由男性和女性定义的人工语言,最初是为了与计算机进行交流,但同样重要的是,用于在人与人之间交流算法.
例如,语言定义由三个部分组成:
语法:编程语言的语法是定义符号组合的规则集,这些符号组合被认为是该语言中正确结构化的程序。语言的语法定义了它的表面形式。基于文本的编程语言基于字符序列。文本语言的词汇语法指定如何将字符分块为标记。语法是指组合符号以在语言中创建结构良好的句子(或程序)的方式。语法定义了语言成分之间的形式关系,从而提供了构成语言中合法字符串的各种表达式的结构描述。语法仅处理语言中符号的形式和结构,而不考虑其含义,语法是仅关心句子本身是否对语言语法有效的概念
语义:语义是关于句子是否具有有效含义。语义揭示了语言中语法上有效的字符串的含义。对于自然语言来说,这意味着将句子和短语与我们经历的对象、想法和感受相关联。对于编程语言,语义描述了计算机在执行该语言的程序时遵循的行为。我们可以通过描述程序的输入和输出之间的关系或通过逐步解释程序如何在真实或抽象机器上执行来公开这种行为。语义学是意义研究的总称。在计算机科学中,编程语言语义的主题旨在为程序赋予精确的数学含义。
在自然语言中,一个句子可能在语法上是正确的,但在语义上却毫无意义。例如,句子:
“这个男人从商店买了无限。”
这在语法上是正确的,但在现实世界中没有意义。同样,在编程语言中,语句可能在语法上正确,但在语义上不正确,因为它违反了语言的规则或其预期含义。
在编程的低级语义中,我们关心的是根据语言的类型系统或其他规则,具有正确语法的语句是否也有意义。即使语法可能有效,操作的语义也可能无效。静态类型语言(如 java)中的类型系统有助于在运行时之前强制执行这些规则,但动态类型语言(如 javascript)并不总是在运行时强制执行这些规则。
在松散类型的 javascript 中,您可能没有更严格的类型系统带来的保护,并且该语言将允许某些在语义上没有意义的操作。考虑以下 javascript 代码:
let name = "alice"; name = 42; // no error, but semantically this doesn't make sense.
这里,语法完全有效,并且 javascript 允许赋值,但从语义上讲它很奇怪。您尝试将数字 (42) 分配给可能用于保存字符串(名称)的变量。 javascript 中没有类型检查来阻止您犯此错误,但这是一个低级语义错误,因为它与开发人员的意图不一致。
如果我们实施它。它被执行了。没有任何错误!
在更严格的语言中,例如 typescript 或 java,这会在编译期间立即触发类型错误。
typescript 是 javascript 的超集,引入了类型检查来防止这些低级语义问题:
let name: string = "alice"; name = 42; // error: type 'number' is not assignable to type 'string'.
在 typescript 中,编译器会检测到语义错误,因为 42 是一个数字,不能分配给声明为字符串的变量。这种类型的强制执行可以保护开发人员免受意外错误的影响。
在 javascript 中,为了避免这些低级语义问题,开发人员经常使用运行时检查:
let name = "alice"; if (typeof name !== "string") { throw new error("expected a string!"); }
尽管 javascript 不强制执行类型,但添加手动检查可以帮助避免错误类型导致程序出现问题的错误。
在更高的层面上,语义关注的是你的程序应该实现的目标。这不仅仅是程序是否具有有效的语法或类型是否正确对齐的问题,而是程序是否按照开发人员预期的方式运行,或者是否解决了其设计要解决的问题。
例如,假设您正在构建一个简单的股票交易系统。代码的高级语义是为了确保系统使用正确的业务逻辑以正确的方式交易股票。即使代码不会产生类型错误或语法错误,它仍然可能无法满足预期的功能。
让我们看一下使用简化的股票交易场景的高级语义的 javascript 示例:
let opentrade = { symbol: "eurusd", direction: "buy", profit: 100 }; function closetrade(trade) { if (trade.profit >= 50) { console.log(`closing trade for ${trade.symbol} with profit of ${trade.profit}`); } else { console.log(`trade for ${trade.symbol} is not ready to close.`); } } // check if a trade is open for eurusd and close it if the profit target is reached. closetrade(opentrade);
语法正确,程序运行没有错误。然而,想象一下我们现在在更高级别引入了一个错误,例如不小心为同一交易品种输入了两笔交易,这违反了系统的业务规则。
let opentrades = [ { symbol: "eurusd", direction: "buy", profit: 100 }, { symbol: "eurusd", direction: "sell", profit: 20 } ]; // check all trades and close any that hit their profit target. opentrades.foreach(trade => closetrade(trade));
在这里,两笔交易都是独立处理的,但系统最终会在同一交易品种上进行两笔交易,一笔是买入方向,一笔是卖出方向。这打破了高级业务规则,即在任何给定时间每个交易品种只能有一笔未平仓交易。
尽管代码执行时没有语法或类型错误,但它在高层语义上不正确。系统逻辑应该确保每个交易品种一次只能有一笔交易处于活动状态。此错误可能会在现实世界的交易系统中导致意想不到的后果,例如财务损失。
要解决此高级语义问题,您需要调整逻辑以确保特定交易品种仅开放一笔交易:
let openTrades = [ { symbol: "EURUSD", direction: "buy", profit: 100 } ]; function openNewTrade(newTrade) { // Ensure no other trades are open for the same symbol. const existingTrade = openTrades.find(trade => trade.symbol === newTrade.symbol); if (!existingTrade) { openTrades.push(newTrade); console.log(`Opened new trade for ${newTrade.symbol}`); } else { console.log(`Cannot open a new trade for ${newTrade.symbol}, trade already exists.`); } } openNewTrade({ symbol: "EURUSD", direction: "sell", profit: 0 });
这里,逻辑确保如果同一交易品种已经有活跃交易,则无法打开新交易。这是一个高级语义修复,因为它解决了程序应遵循的核心业务逻辑,而不是语法或类型问题。
这只是每个的基本信息。欲了解更多信息,您可以阅读以下文章: