1.12「Motoko——Imperative control flow」

2023-11-06 02:59

本文主要是介绍1.12「Motoko——Imperative control flow」,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.12「Motoko——Imperative control flow」

控制流有两个关键类别:

  • 声明性(declarative):当某些值的结构引导控件和下一个要计算的表达式的选择时,如 ifswitch 表达式;
  • 命令式(Imperative):根据程序员的命令突然改变,禁止常规控制流; 例如 breakcontinue ,以及 returnthrow

命令式(Imperative)控制流通常与状态更改和其他side-effects(如错误处理和输入/输出)密切相关。

Early return from func

通常,函数的结果是函数体的值。有时候,在对函数体(body)进行评估的过程中,评估结果在评估结束之前就可以得到。在这种情况下,可以使用 return <exp> 结构放弃计算的其余部分,并立即退出函数得到一个结果。类似地,在允许的情况下,throw可用于放弃带有错误的计算。

当函数具有单位结果类型时,可以使用简写return代替等效的 return ()

Loops and labels:

Motoko提供了几种循环的结构,包括:

  • for表达式用于迭代结构化数据成员。
  • loop表达式用于编程重复(可选带终止条件)的循环。
  • while loops for programmatic repetition with entry condition.While循环用于带有入口条件的编程重复。

其中任何一个都可以用一个label <name>作为前缀,以便给循环一个符号名。命名循环用于强制更改控制流以从命名循环的入口或出口继续。

  • continue <name>重新进入循环,或
  • break <name>完全退出循环。

在下面的示例中,for 表达式循环遍历一些文本的字符,并在遇到感叹号时放弃迭代。

import Debug "mo:base/Debug";
label letters for (c in "ran!!dom".chars()) {Debug.print(debug_show(c));if (c == '!') { break letters };// ...
}

Labeled expressions:

还有另外两个不太主流但在某些情况下很有用的label

  • 可以输入label
  • 任何表达式(不仅限于循环)都可以通过给它添加label前缀来命名;break 允许通过为其结果提供一个直接值来短路(short-circuit)表达式的计算。(这类似于使用 return 提前退出函数,但是没有声明和调用函数的开销。)

带类型注释的标签的语法是label <name> : <type> <expr>。表示任何表达式都可以使用break <name> <alt-expr>结构退出,该结构返回<alt-expr> 的值作为<expr>的值,对 <expr>进行短路(short-circuit)计算。

明智地使用这些结构允许程序员专注于主程序逻辑,并通过 break 处理异常情况。

import Text "mo:base/Text";
import Iter "mo:base/Iter";type Host = Text;
let formInput = "us@dfn";let address = label exit : ?(Text, Host) {let splitted = Text.split(formInput, #char '@');let array = Iter.toArray<Text>(splitted);if (array.size() != 2) { break exit(null) };let account = array[0];let host = array[1];// if (not (parseHost(host))) { break exit(null) };?(account, host)
}

自然地,被label标记的普通表达式不允许continue.在类型方面,&lt;expr&gt;和 &lt;alt-expr&gt;​的类型必须符合label的&lt;type&gt;定义。如果一个标签只被赋予了一个&lt;name&gt;, 那么它的 &lt;type&gt;默认为元组(unit)(()). 类似地,没有 &lt;alt-expr&gt; 表达式的break 是返回值unit***(())的break*表达式的简写。

Option blocks and null breaks:

像许多其他高级语言一样,Motoko 允许您选择null值,跟踪在使用表单的选项类型?Tnull值的可能发生情况。这既是为了鼓励您尽可能避免使用null值,也是为了在必要时考虑null值的可能性。

如果测试null值的唯一方法是使用详细的switch表达式,那么后者可能会很麻烦,但 Motoko 使用一些专门的语法简化了对选项类型的处理: option ***blocks***和 null******break

选项块do ? &lt;block&gt;产生一个?T类型的值,同时块&lt;block&gt;的类型为 T,并且重要的是,引出了来自&lt;block&gt;的一个break的可能性。在一个 do ? &lt;block&gt;里,the null break&lt;exp&gt; !(PS:下方重点记号处有讲解)测试了表达式的结果?U(U为一个不相关的类型)是否为null。如果&lt;exp&gt;的结果是null,则控制立刻携带着null值离开do ? &lt;block&gt;。否则,&lt;exp&gt;的结果一定是一个选项类型值?v,并且&lt;exp&gt; ! 会继续对?v的内容v(类型为U)进行评估。

——原文为"The option block, do ? &lt;block&gt;, produces a value of type ?T, when block &lt;block&gt; has type T and, importantly, introduces the possibility of a break from &lt;block&gt;. Within a do ? &lt;block&gt;, the null break &lt;exp&gt; !, tests whether the result of the expression, ‘’, of unrelated option type, ?U, is null. If the result &lt;exp&gt;is null, control immediately exits the do ? &lt;block&gt; with value null. Otherwise, the result of &lt;exp&gt; must be an option value ?v, and evaluation of &lt;exp&gt; ! proceeds with its contents, v (of type U)."

作为一个实际的例子,我们给出了一个简单的函数 eval 的数值 Exp 表达式的定义,这个表达式是由自然数、除法和零测试构成的,编码为一个变量类型:

type Exp = {#Lit : Nat;#Div : (Exp, Exp);#IfZero : (Exp, Exp, Exp);
};func eval(e : Exp) : ? Nat {do ? {switch e {case (#Lit n) { n };case (#Div (e1, e2)) {let v1 = eval e1 !;let v2 = eval e2 !;if (v2 == 0)null !else v1 / v2};case (#IfZero (e1, e2, e3)) {if (eval e1 ! == 0)eval e2 !elseeval e3 !};};};
}

为了防止在不捕获的情况下被除0eval 函数返回一个选项结果,使用 null 表示失败。

每个递归调用都使用!(单目运算符,前加一个<exp>组成null break,个人的理解是当操作数为null时,“返回”break,退出当前控制流/块

)。当结果为 null 时,立即退出外层的do ? block,也因此退出函数本身,以null为返回值。

Repetition with loop

无限重复命令表达式序列的最简单方法是使用loop循环结构。

loop { ⟨expr1⟩; ⟨expr2⟩; ... }

只能使用 returnbreak 结构退出循环。

可以附加一个重返条件,允许有条件地重复使用 loop &lt;body&gt; while &lt;cond&gt;循环。

特点:这种循环的主体(body)总是**至少执行一次**。

while loops with precondition:

有时需要一个入口条件来保护循环的第一次执行。对于这种循环,可以使用while &lt;cond&gt; &lt;body&gt;结构。

while (earned < need) { earned += earn() };

特点:与loop循环不同,while循环的主体**可能永远不会执行**。

for loops for iteration:

可以使用 for 循环对某个同类集合的元素进行迭代。这些值从迭代器中获取,并依次绑定到循环模式(pattern)。

let carsInStock = [("Buick", 2020, 23.000),("Toyota", 2019, 17.500),("Audi", 2020, 34.900)
];
var inventory : { var value : Float } = { var value = 0.0 };
for ((model, year, price) in carsInStock.vals()) {inventory.value += price;
};
inventory

Using range with a for loop:

Range 函数生成一个迭代器(Iter&lt;Nat&gt;类型) ,其中包含给定的上下界。

下面的循环示例在十一次迭代中输出数字0到10:

import Iter "mo:base/Iter";
import Debug "mo:base/Debug";
var i = 0;
for (j in Iter.range(0, 10)) {Debug.print(debug_show(j));assert(j == i);i += 1;
};
assert(i == 11);

更一般地说,range函数是一个在自然数序列上构造迭代器的class,调用后返回一个迭代器,每个这样的迭代器都为Iter&lt;Nat&gt;类型。

作为一个构造函数,range 有一个函数类型:

(lower:Nat, upper:Nat) -> Iter<Nat>

其中 Iter&lt;Nat&gt;是一个迭代器对象类型,next方法生成可选的元素,每个元素类型为?Nat

type Iter<A> = {next : () -> ?A};

对于每次调用,next返回一个可选元素(类型为?Nat)。

null 表示迭代序列已终止。

到达null之前,每个形式为 ?n 的非null值中的 n 包含迭代序列中的下一个连续元素。

Using revrange

Like range, the function revrange is a class that constructs iterators (each of type Iter&lt;Nat&gt;). As a constructor function, it has a function type:

range类似,revrange函数是一个构造迭代器(每个类型都为 Iter&lt;Nat&gt;)的类。作为一个构造函数,它有一个函数类型:

(upper:Nat, lower:Nat) -> Iter<Nat>

不像rangerevrange函数在其迭代序列中递减,从初始上界到最终下界。

Using iterators of specific data structures:

许多内置的数据结构都有预定义的迭代器:

TypeNameIteratorElementsElement type
[T]array of T​svalsthe array’s membersT
[T]array of T​skeysthe array’s valid indicesNat
[var T]mutable array of T​svalsthe array’s membersT
[var T]mutable array of T​skeysthe array’s valid indicesNat
Texttextcharsthe text’s charactersChar
Blobblobvalsthe blob’s bytesNat8

用户定义的数据结构可以定义自己的迭代器。只要它们符合元素类型A对应的的Iter&lt;A&gt;类型,它们的行为就像内置的元素,可以与普通 for循环一起使用。

Unknown-54
- Motoko -

这篇关于1.12「Motoko——Imperative control flow」的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/354090

相关文章

GNSS CTS GNSS Start and Location Flow of Android15

目录 1. 本文概述2.CTS 测试3.Gnss Flow3.1 Gnss Start Flow3.2 Gnss Location Output Flow 1. 本文概述 本来是为了做Android 14 Gnss CTS 的相关环境的搭建和测试,然后在测试中遇到了一些问题,去寻找CTS源码(/cts/tests/tests/location/src/android/locat

MFC中Spin Control控件使用,同时数据在Edit Control中显示

实现mfc spin control 上下滚动,只需捕捉spin control 的 UDN_DELTAPOD 消息,如下:  OnDeltaposSpin1(NMHDR *pNMHDR, LRESULT *pResult) {  LPNMUPDOWN pNMUpDown = reinterpret_cast(pNMHDR);  // TODO: 在此添加控件通知处理程序代码    if

2024年 Biomedical Signal Processing and Control 期刊投稿经验最新分享

期刊介绍 《Biomedical Signal Processing and Control 》期刊旨在为临床医学和生物科学中信号和图像的测量和分析研究提供一个跨学科的国际论坛。重点放在处理在临床诊断,患者监测和管理中使用的方法和设备的实际,应用为主导的研究的贡献。 生物医学信号处理和控制反映了这些方法在工程和临床科学的界面上被使用和发展的主要领域。期刊的范围包括相关的评论论文(review p

Understanding the GitHub Flow

这里看下Github的入门介绍    --链接 GitHub Flow is a lightweight, branch-based workflow that supports teams and projects where deployments are made regularly. This guide explains how and why GitHub Flow works

Versioned Staged Flow-Sensitive Pointer Analysis

VSFS 1.Introduction2.Approach2.1.相关概念2.2.VSFS 3.Evaluation参考文献 1.Introduction 上一篇blog我介绍了目前flow-sensitive pointer analysis常用的SFS算法。相比IFDS-based方法,SFS显著通过稀疏分析提升了效率,但是其内部依旧有许多冗余计算,留下了很大优化空间。 以

Salt Function Flow:深度解析复杂网关编排的优势与实践

系列文章索引: Salt Function Flow 系列文章 在业务流程编排中,处理条件逻辑、并行任务、以及复杂的流程分支是常见的挑战。对于需要高度灵活性和扩展性的项目,Salt Function Flow 提供了强大的网关编排能力,使开发者能够轻松定义和管理复杂的业务流程。本文将深入探讨Salt Function Flow中的复杂网关编排功能,展示其如何通过排他网关、并行执行等功能应对复杂的

Salt Function Flow 系列文章

Salt Function Flow 是一款Java开发、轻量级、内存级的业务流程编排框架,旨在帮助开发者通过函数式编程的方式定义和管理复杂的业务流程。它以高效、灵活的流程处理为核心,适用于多种业务场景,从简单任务自动化到复杂业务逻辑处理。 系列文章: Salt Function Flow:深度研发经验的沉淀,打造轻量级高效流程编排框架 Salt Function Flow:深度解析复杂网关编排

解决Vue请求 ‘No 'Access-Control-Allow-Origin' header is present on the requested resource’错误

如果我们用VueResouce直接请求,这样写(以豆瓣api为例): this.$http.get('https://api.douban.com//v2/movie/top250').then((response) => {this.movie = response.data;console.log(this.movie); }); 就会报错: 因为这是一个跨域的请求,不能直接

C# 窗体中Control以及Invalidate,Update,Refresh三种重绘方法的区别

在 C# 中,Control 类是 Windows Forms 应用程序中所有控件的基类。它提供了控件的基本功能和属性,这些功能和属性被所有继承自 Control 类的子类所共享。这意味着 Control 类是构建 Windows Forms 应用程序中用户界面元素的基础。 以下是 Control 类的一些关键特性和方法: 属性: Size:获取或设置控件的宽度和高度。Location:获

Kotlin 流 Flow

挂起函数可以异步地返回一个值,而对于返回多个值,可以使用流,使用 emit(x) 发射多个值, collect { } 来收集值。 默认 流是冷的,只有 收集(collect) 时才会执行。 1. 流的创建 flow {} 生成流,emit(x) 来发射值;xxx.asFlow() 集合转成Flow;flowOf(1, 2, 3) 生成固定值的流。 1.1 flow {} flow {}