课程8——状态通道和方法保存数据
到现在为止,你已经很擅长于发送数据到元组空间和从元组空间中获取数据。但是无论你在什么时候进行计算,你有时需要把一些数据放在一边晚点才使用。几乎所有编程语言都有变量的概念。
rholang 的另一个独特的地方在于它没有传统的变量。然而,我们只是使用元组空间来保存我们的数据。只要你想要把一些数据放在一边晚点使用,就把数据发送到一些通道里然后晚点再从通道中获取。以这种方式使用的通道叫做"状态通道",通常我们会在状态通道名字末尾用Ch
box.rho
什么数据发送到@somePublicChannel中?
什么数据留在了boxCh 状态通道里?
0
@"somePublicBox"
42
它里面是空的
持久化数据如果我们再次检查通道,我们会得不到数据。因为一旦我们接收了信息,信息就会从元组空间中取出。我们在上面的课程简短地讨论过解决方案。
persistentBox.rho
什么数据发送到@somePublicChannel中?
什么数据保留在boxCh状态通道里?
0
@"somePublicBox"
42
它里面是空的
重访耐心游戏在几节课钱,我们讨论了一个耐心游戏,游戏里的玩家希望成为最后一个人发送信息到合约里面。之前我们遇到了一些问题因为我们无法保证游戏的结果能输出。
花几分钟重新看看想想我们之前遇到的问题。现在使用这个状态通道,我们可以适当地解决这个问题。
patience.rho
上面的代码是如何解决每一块代码只调用一次的问题?
通过强制玩家1使用顶部的代码
因为在第一次调用后,activeCh 是空的
因为状态通道使代码顺序执行
对象和方法在如Java这样的"面向对象编程" 语言, 我们可以通过封装一些使用的数据和修改数据的方法到真实世界的对象上建立模型。在rholang里面同样的事情也是可能的。
在这个例子里面,我们会创建一个对象代表一个基本的点击计数器。需要的部件有:
counter.rho
练习如果每次检查时,都要我手动更换计数值会是很不方便的。取而代之的我们应该有一个方法做那个事。
练习现在更加简单和安全地去检查计数器。让我们一些更好的测试来测试所有的方法。
工厂如果你曾经用其它像Java这样的语言,那你可能会很熟悉构造器。如果你曾经用过Java,那非常好,因为rholang 用工厂来创建新的对象而不是构造器。
在rholang中创造一个计数器是很有用的,你将来很可能会在你的项目中用到。现在的问题是很多项目都可能使用计数器,只有一个可能是不足够的。所以解决方法是创建一个工厂合约创建计数器。当一个工厂合约被调用,它会返回一个新的计数器。
counterFactory.rho
用户怎么调用工厂才可能得到一个新的计数器?
counterFactory!(*ack)
counterFactory!(0, *ack)
@"counterFactory"()
counterFactory!(myIncrease, myReset)
如果用户按照上面方法创造一个计数器,用户怎么重置他们的计数器。
myIncrease!(*ack)
myReset!(*ack)
counterFactory!(myReset, *ack)
counterFactory!(myReset)
方法调度这里有两种主要的技术使方法可用。第一种方法我称为"方法独立" 因为每一个方法都监听它们自己专用的通道。
另一种方法是"控制面板" 技术, 这技术会用一个单独的不可伪造name做一个叫做控制面板的东西,然后所有方法都在此基础上建立。
// Separation of Powerscontract factory(method1, method2) = { contract method1(ack) = { ... } contract method2(ack) = { ... }}// Control Panelcontract factory(cPanel) = { contract @[cPanel, "method1"](ack) = { ... } contract @[cPanel, "method2"](ack) = { ... }}复制代码计数器示例使用哪种技术来调度方法?
练习用另一种调度方法来转换上面的例子。你也应该把测试也转换了。
课程9——对象功能最近我们学习了如何用不可伪造的"names"来限制知道这个"name"的人来使用这个通道。我们也见识了如何用状态通道来保存数据,还有如何用将方法用于修改封装数据。在这节课中,我们将会学习如何将方法放置在不可伪造的"name"上,设计出一种非常有用的设计模式--“对象功能”。
对象功能的日常例子就是你家或者车的钥匙。你可以通过使用这个对象的功能来进入你的家里或者启动你的汽车。你也可以通过给予你的钥匙或者你钥匙的复制品给别人,让别人也拥有那些功能。
在这一节课中,我们将会使用状态通道和对象功能来创建一些例子工程。我们可以看到对象功能比bundle赋予读写数据权利的方式更常用,还可以使用更抽象的功能如重置计数器或者删除一个facebook账户。
重访空中交通管制塔让我们现在再看一下第四课的空中交通管制的例子。先前的控制者可以通过使用一个持续性的发送来广播天气和跑道信息。但是他们不能够更新信息。我们都知道天气是经常无法预测地变化。所以在这个例子中,我们将要保存现在的信息在状态通道里面,然后给控制者一个功能来按需要更新信息。
atc.rho
使用一个只读的bundle来调整到站里似乎更加自然。然而,如果我们使用budle,然后第一个接收信息的监听者可能会取出在状态通道的信息。然后就不能给其它飞行员接收这个信息。为了能确保消息可以按我们想的那样持久化,我们要处理所有到状态通道的权限,然后只能给飞行员去获取正确信息的功能。
空中交通管制是如何跟新信息的?
set!("Strong crosswinds, be advised")
setInfo!("Strong crosswinds, be advised")
getInfo!("Strong crosswinds, be advised")
stationFactory.setInfo!("Strong crosswinds, be advised")
练习写更多全面的测试以确保空中交通管制可以成功地更新信息而飞行员不可以。 Write more thorough tests to make sure the ATCs can update the status successfully, and that pilots cannot
储蓄账户在这个例子里,我们要用rholang写代码来模拟一个简单的储蓄账户。它有存入,提取和查看的方法。
不想我们的计数器,储蓄账户必需要安全的。我们也不想其他人知道我们有多少钱,更不想别人能拿我们的钱。
这里是一些需要考虑的初步的代码。 savingsStarter.rho
练习在账户代码里把剩下的方法代码补上。
下面哪个合约是被当做工厂的?
check
withdraw
deposit
openAccount
我们当前的储蓄账户允许负的存款,但是可能它不应该这样的。请想一想你应该怎么去解决这个问题。我们将会在下一节课学习一样合社的工具来完成这个事。
盗取资金尝试写出Eve需要盗取Sarah资金的代码。我敢打赌你想不出任何方法。因为只有Sarah有权限使用控制账户的不可伪造的"name"。
如果Sarah想要允许别人存钱到她的银行账户,但是不可以查看和提取,她应该怎么创建她的账户?
openAccount!(10, *"sarahDeposit", *sarahWithdraw, *sarahCheck)
openAccount!(10, @"sarahDeposit", *sarahWithdraw, *sarahCheck)
openAccount!(10, @"sarahDeposit", @"sarahWithdraw", @"sarahCheck")
openAccount!(10, *sarahDeposit, @"sarahWithdraw", @"sarahCheck")
两种工厂到目前为止,我们所有的工厂方法都要求我们传入“names”来创建合约。在储蓄账户的例子中,这些"names"是 check, deposit 和 withdraw。我把这种工厂叫做“自我创建”工厂,简称"BYOC"。这种BYOC技术有一个优势,就是用户可以提供任何她喜欢的从别的合约来的或者公共的"names"。
另一种技术是允许工厂创建需要的不可伪造的"names"然后发送给回调的人。我把这种方式成为“全面服务”工厂。如果你不要求有灵活传入随意"names"的功能,一个全面服务的工厂可能更不麻烦。
练习把例子中的工厂模式转化为另一种模式
现在你已经装换了储蓄账户的代码,Sarah还有可能让别人给她的账户存款么?
不可以;她不再可以传入一个公共的“name”
不可以;她没有权利这么做
可以;她只需要自己把新的功能公开。
可以;就像以前一样
可中止的火箭发射回到我们学到join操作的时候,我们假想了一个情况,两个操作员必须同时许可火箭发射。我们希望他们也可以收回发射许可。
这个问题的解决方法是,当他们允许火箭发射的时候,可以给操作员一个中止按钮。
abortableLaunch.rho
练习完成上面代码中bob的发射逻辑并且测试。
设计模式还有很多常用的对象功能设计模式。你可以在A Picturebook of Secure Cooperation中找到很多例子解释说明。
练习我们会在以后遇到的例子中遇到很多这样的设计模式,但是我鼓励你尝试自己用rholang实现1到2个。
课程10——更多语法到目前为止你已经成功写出一些实际的项目。你就应该这么做!在我们深入下一单元前,让我们学习更多实用的语法,然后你需要的现实世界的工具来创建下一代杀手级应用。
二元运算符第一个学习的语法就是二元运算符。二元运算符需要有两个操作数,那是他们为什么成为“二元”的原因。大多数都是用于算术。请看下面的例子。
math.rho
练习现在轮到你写出f2c的合约。你可以使用相同的两个测试案例以确保你的结果正确。
最后你应该知道的二元操作符是 ++ ,它是用于“联合”或者说把两个小东西组合成一个大东西。这个操作符适用于列表和字符串。列表我们会在下一单元学习到,字符串我们已经学习过了。
greeter.rho
代码 stdout!("I" ++ "<3" ++ "rholang") 会输出什么?
I <3 rholang
["I", "<3", "rholang"]
I<3rholang
I++<3++rholang
接收 Processes?我们通常发送 ____ 和接收 ____.
processes, names
processes, processes
names, names
names, processes
没有严格规定
要做的事情:有一种站在门口的邮箱柜子。我们可能会把在你家门口的邮箱或者邮件槽什么的弄混。“嘿,兄弟,还是账单?我想要情书”。
那只是重温一下以前单元的内容。希望你还记住吧。如果你已经写过你自己的rholang代码,那么你可能已经发现你是多么的希望你可以直接接收"Processes"而不需要输入各种*。这是很普遍的一种现象,很幸运rholang有一个很好的解决方法。我们经常接收"names",当时我们可以通过@myValue这里来绑定"name"语法。既然@myValue是一个"name",那么myValue一定是一个"process"。
这个语法允许我们做像这样的事, `for (@number <- @"someChan"){@"double"!(2 * number)}``
应该用什么样的代码来与前面的代码并行以达到24在@"double"中?
@number!(12)
@"someChan"!(12)
@number!(24)
@"double"!(48)
练习重看一下第三课中传音筒的游戏,那个展示我们已经使用了@message的语法模式,所以message是一个"process"
我们应该把 for(@x <- @y){stdout!(...)} 中 ... 替换为什么来让程序是正确的?
条件语句几乎在所有编程语言中,程序的行为需要根据情况来定义。例如,如果天气很好的话,我就可以跑在小道上面,但是如果下雨的话,我就在路边。rholang是这样用语法实现的。
if ( /* condition */ ) { Nil // Do this if condition is true}else { Nil // Do this if condition is false}`复制代码你用if的情况是没有限制的,包括猜一个秘密字母的正确,在游戏中设定一个高分数,决定哪张扑克牌更大和计算选举胜者。下面的例子合约展示给你看如何检查一个银行账号的状态。
signTest.rho
练习The accounting program has a problem. It says that accounts with a balance of zero are overdrawn. But really zero should be in good standing. You can fix this using the "greater than or equal" operator, >=. Make sure you add a few tests to make sure it works.
比较符现在你知道了如何使用if/else,这里还有很多比较符给你使用。
- a > b a大于b么?
- a < b a小于b么?
- a == b a等于b么?
- a <= b a小于或者等于b么?
- a >= b a大于或者等于b么?
- a != b a不等于b么?
这些操作符可以作用于数字或者字符串。字符串按照字典顺序排序,有点像字母表排序。但是小心!如果你用一个数字与一个字符串比较,它会是另一个停止的"process"。 These operators work on both numbers and text strings. Text strings are sorted lexicographically, which is a lot like alphabetically. But be careful! If you try to compare a number to a string, it will just be another stopped process.
下面哪个是对的?
4 < 3
"b" < "a"
5 <= 6
"hello" != "hello"
练习写一个rholang程序,要求用户发送他们的名字。在多数情况下,合约可以简单地回复“hello”,但是如果他们的名字与你的一样,你要告诉他们。
布尔操作符Rholang也有传统的布尔操作符, AND, OR, 和 NOT。语法是
- 当 a和b都是真的,a and b 是真的
- 当 a或b都是真的,a and b 是真的
- 当 a是假的 ,not a 是真的
stdout!(true and true) 会输出什么??
stdout!(not true) 会输出什么?
stdout!((not not true) or false) 会输出什么?
练习写一个合约告诉调用的人他们是否有资格投票。你必须达到一定的年纪和在一个规定的国家内才能投票。你可以选择年龄和国家。我可以通过并行@"canIVote!("Nigeria", 30)" 来使用合约。
练习上面的合约指适用于一个特定的国家。用我们学过的工厂来创建出有各种资格的检查者。创建一个投票年龄为18岁的加拿大的检查者,用@"checkerFactory"!(canadaChecker, "Canada", 18)使用。然后用代码canadaChecker!("Estonia", 41)测试一个41岁的爱沙尼亚人他是否符合加拿大的投票资格。 提示:他不能在加拿大投票。
课程11——模式匹配关于模式模式其实经常出现在我们的日常生活中。如果你看到一辆你从来没有见过的新型车而你仍然知道那是一辆车因为它匹配你心里对车的模式的定义,4个轮子,两个或者4个门还有挡风玻璃。
相似地,我们可以匹配句式模式。句子“我喜欢芝士”和“我喜欢熊猫”都匹配模式“我喜欢____”。但是“我有一只狗”就不匹配那样的模式。Rholang允许程序员使用模式匹配来控制程序的运行。就是说,不同的代码能根据一个“process”是否匹配一个特定的模式来执行。
match 结构rholang使用模式匹配最明显的一个地方就是它的match结构,它的工作原理请看下面。
matching.rho
在这个代码里,任何一条从patternMatcher通道中获取的信息x代表一个标记的"process"。我们拿"process"(未标记)与一些下面的模式比较一下,然后看看那些模式是匹配的。下划线仅仅是用来填空的,它可以匹配任何模式。它被称为"通配符",你可以经常看到它在match结构中用作最后的模式匹配以保证如果没有模式匹配的话就使用默认的情况处理。
模式匹配也可以在使用for或者contract时候使用。为了让信息成功被接收,发出去的信息必须匹配应该要被接收的模式。我们一会可以看到一个这种用途的例子。
我们看到的两种情况我们有时候会使用下划线。例如我经常会使用for (_ <- ack),这样代表ack通道里的信息无论匹配什么模式都接收。当你只关心你是否接收一个信息而不是接收信息的内容的时候就可以用下划线来表示,这是一种标准用法。
我们也可以在前面我们学习到如何接收“processes”的课程中的使用模式匹配。当我们写for (@p <- x) 时候,这代码的意思是接收所有匹配标记process模式的信息,然后把“process”绑定到p。
p在x!("hello") | for(@p <- x){Nil} 中会绑定到什么样的变量
p在x!("hello" | 4) | for(@{"hello" | p} <- x){Nil} 中会绑定到什么样的变量
语法糖
其实我们完全可以用模式匹配来替换到之前我们学习到的 if/else。事实上,那正是 if/else 内在实现的方式。因为那个只是其他语法的速记,所以if/else也可以说是语法糖。下面的两段代码其实作用完全一样。
if (cond) { // Do Process P}else { // Do Process Q}复制代码into
match cond { true => // Do Process P false => // Do Process Q}复制代码练习用march而不是if/else来重写我们前面课程的signTest.rho的例子
我们应该如何用if/else重写上面第一个match的例子?
仅仅只是反转前面的练习
以if (x == a|b)开始
我们无法重写因为上面的例子比只是true和false更复杂
一个更友好的招待员让我们制作一个更友好的招待员,这个招待员即使我们不告诉他我们的名字,他也会说你好。关键部分在于我们有两个不一样的合约在监听同一个greeter通道。 greeter2.rho
你可能可以明白每个合约单独是如何运行的。有意思的部分是如何让rholang决定当信息从greeter通道过来的时候实际上哪一个合约去执行。方法在于rholang应该根据哪一个合约的参数匹配我们发送的模式。如果我发送两个参数,招待员就使用两个参数来调用。如果我只发送一个参数,那么合约就应该使用一个参数来调用。在未来,rholang将会支持发送参数的类型做模式匹配。
练习写一系列的合约来计算长方形的面积。在大多数明显的情况下,调用的人需要提供长度和宽度。但是用户可能也只提供单个宽度,这种情况通常长方形实际上是一个正方形,长宽都是提供的的宽度长度。最后,用户也可能什么都不提供,这种情况就是长方形是1x1的正方形。
高级模式匹配你可以通过模式匹配做一些有意思的事情,比如for(@{x!(P)} <- y){ Q }只会在"process"在频道x发送匹配单独发送模式的时候才会计算。然后在process Q 里面你将会绑定变量 x这个频道和 p这个被发送的process。
sendASend.rho
x!("hello") | for ({P | Q} <- x){Nil} 会产生一个通信事件么?
会的,因为发送和接收在同一个通道
会的,因为发送和接收在同一个通道并且模式匹配
不会的,因为发送和接收在不同一个通道
不会的,因为发送和接收在同一个通道但是模式不匹配。
Unions and Intersections可能有时候你想要匹配两种模式中的一种或者同时匹配两种模式。这些操作都与我们使用上一节课讨论的布尔运算相似,但是我们当我们要模式匹配时要使用不同的运算符。
你可以用"unions"操作符,/来匹配其中一种模式。
union.rho
使用"intersection"操作符来同时匹配两个模式。在这个例子里,我们检查注册数据是否有效。一个注册者必须提供他们的名字和年龄,可能需要提供一些额外的数据。顺便说一句,这项保存键值得技术经常也成为"RHOCore"。 To match both of two patterns you use the "intersection" operator, /. In this example we are verifying that registration data is valid. A registrant must supply their name and age, and may supply any amount of additional data. By the way, this technique for storing key-value data is often known as "RHOCore".
intersection.rho
练习这个union语法的例子是相当基础的。扩展那个代码使它可以匹配更多的语言和更多的单词。同时也写出测试来展示当只有默认模式被匹配时候会发生什么。
在这一节当中讨论的逻辑连接与绑定有一点关系,但是那超出本教程的范围。我鼓励你通过例子程序来做式样,当你可以的时候可以参考rholang使用手册。
更多关于Bundles几节课前,我们讨论了怎么使用bundles来创建可读或者可写的通道。但是我们还没有讨论过他们的同名特征。Bundles可以用来绑定组合的名字,让他们在做模式匹配的时候无法分开。
在这个例子中,一个军队有一个导弹,他们通过创建在一个不可伪造的"name"上创建功能来保持对导弹的控制。由于外交关系,军队需要允许公众检查导弹的安全,但是不能发射它。
missileUnsafe.rho
练习军队在这里犯了一个很严重的错误,所有人都可以发射他们的导弹。请想一个让外界发射导弹的方法。
答案: missileAttack.rho
为了解决这个问题,军队只要简单地给出一个bundled版本的组合"name",让他们无法再模式匹配中分开。
missileSafe.rho
当攻击在安全的代码上运行时,会有什么输出?
"launching..."
"inspecting..."
"failed to launch..."
什么都没有
课程12——数据结构It is common for programs to process and store real world data. And whenever you have lots of data it is important to keep it organized so you can find the information you need quickly. In the analog world, paper files are kept organized by stacking them, putting them in folders, and file cabinets. The same concept applies in programming, and rholang is no exception (for once!).
If you've never seen data structures before, you will likely want to consult some other references, and look at additional example code.
String MethodsLet's start with a familiar idea. We've seen strings since the very first program in lesson one. Really strings are just a nice way to organize a bunch of characters, and that makes them a data structure. Like all data structures, strings have "methods" that you can perform on them.
String's length method tells how many characters are in a string. While it's slice method creates a new string with some characters sliced off of each end. Strings also support the ++ operator for concatenation. wordLength.rho
What is the result of "hello world".length()?
2
10
11
undefined
"hello"
Which of the following evaluates to "ello"?
"hello world".slice(1, 5)
"hello world".slice(0, 5)
"hello world".slice(1, 4)
"hello world".slice(3, 6)
Strings also have a method called hexToBytes that is designed to work on strings that contain valid hexadecimal numbers. It gives back a byte array that is represented by that hex number. Try to run "1241243e".hexToBytes()
Pro tip: It is also possible to slice a byte array. Experiment with that on your own.
TuplesTuple can rhyme with either "couple" or "drupal"; both pronunciations are correct. You've seen tuples before when you wrote contracts that take in multiple arguments like contract c(x, y, z) = { Nil }. The number of items in a tuple is know as its arity. So the tuple received by contract c is arity three.
Tuples contain several pieces of data in order. They are always a fixed arity, and have relatively few methods. Thus they are the least interesting data structure, but at the same time, the most fundamental. Let's look at some of the methods offered by tuples.
tuple.rho
What is the arity of [3, 4, 9, Nil]?
What would ("a", "b", "c").nth(3) evaluate to?
3
That's an error
"c"
("a", "b", "c")
ExerciseWrite a program that takes in a 4-tuple and prints elements 0 and 3 to the screen.
ListsLists are a lot like tuples, but they are made with square brackets instead of parentheses. They also have more methods, and can be concatenated or glued together using the ++ operator just like strings can. Here are examples of all of list's methods.
list.rho
ExerciseImplement the body of the following running log contract. The user will call the contract every time they go for a run passing in the distance that they ran. The contract will keep track of all the runs in a list. You may also write methods to get all the run data, or get the total distance the user has run.
new logRun, runsCh in { // No runs to start with runsCh!([])| contract logRun(distance) = { // Your code here }}复制代码SetsSets are similar to lists in some ways, but the one big difference is that sets are not ordered. A set is a collection of processes, but there is no first or last item in the set. There are also no duplicates allowed in sets. Let's take a look at some of set's methods.
set.rho
Which code would produce a set of all club members who have not paid their dues?
allMembers.diff(paidMembers)
paidMembers.diff(allMembers)
paidMembers.union(allMembers)
paidMembers.contains(allMembers)
What is the result of Set(1,2,3) == Set(3,2,1)
true
false
invalid syntax
Set(2)
MapsMaps are a lot like sets but they contain key value pairs. Maps are also unordered, but when you add an item (which is now known as a key) you also add an associated value. Here are examples of all of map's methods.
map.rho
What is the result of {"years": 1, "weeks": 52, "days": 365}.get(52)
To demonstrate the usefulness of maps in rholang, let's consider this contract that looks up the capital of any country (that I bothered to type).
capitalOf.rho
ExerciseStarting from the example code above, make a Countries and Capitals quiz game where the user calls up a contract and get's back a challenge country as well as an answer channel. The user then sends her best guess for that country's capital back over the answer channel and gets back a boolean for whether she was correct.
To learn how to use this game interactively with a nice user interface, check out some dapp development examples such as the nth caller game
ExerciseMap's diff method takes another map as an argument. What happens if the diff map has some of the same keys but with different values associated. For example:
{"a": "A", "b": "B", "c": "C"}.diff({"a": 25})复制代码Method Summary TableThat was a lot of info about data structures in one go. So here is a handy table to remind you what methods exist. This info is also on the cheat sheet.
MethodTupleListMapSet
nthxx
toByteArrayxxxx
unionxx
diffxx
addx
deletexx
containsxx
getx
getOrElsex
setx
keysx
sizexx
lengthx
slicexSending and Receiving on Compound NamesWe've learned about several interesting data structures in this lesson. Data structures are processes just like integers, booleans, and Nil. So they can be quoted and turned into names like all those other processes. We can build contracts on those names just like we can any other names. Names that are built on data structures such as tuples are often called compound names.
In this example, Alice and Bob each have one unforgeable name (that I've called key). The keys may be useful on their own (for things not shown in the snippet), but only when used together, can the contract shown be called. This is known as "rights amplification".
new alice, bob, key1, key2, stdout(`rho:io:stdout`) in { alice!(*key1)| bob!(*key2)| contract @(*key1, *key2)(_) = { stdout!("Congratulations, Alice and Bob, you've cooperated.") }}复制代码What tuple is used to build the compound name in contract @(*self, "getVal")(_) = { Nil }?
self
"getval"
(*self, "getVal")
@(*self, "getVal")
@"getVal"
课程13——迭代Let's start with some exercises to review old topics and motivate the problem we're solving in this lesson.
ExerciseWrite a program that prints a countdown of the numbers 3, 2, 1 in order on the screen.
ExerciseNow countdown from 5.
ExerciseWrite a contract that returns a random number between 1 and 3. Hint, you can use race conditions to your advantage here.
ExerciseNow make it choose a random number between 1 and 10.
Is this method of writing a random number generator sustainable?
IterationTODO Julie drawing for iteration.
Iterating is the process of ..... Many programming languages use iteration as a fundamental way of controlling the flow of their programs. Iteration inherently means doing a process to one item then the next then the next. Because rholang is a fully concurrent programming language this is impossible. But that's actually a strength!
manually iterate through the list [1, 2, 3, 4]复制代码This process is clearly not sustainable because long lists would be extremely deeply nested. Worse, any code that we actually write would have a maximum depth. And we don't want to limit the length of our list. Consider this crafty code
Simple recursion that passes a counter and compares it to the lists length复制代码Better version that uses pattern matching to detect empty list复制代码RecursionTodo Julie drawing about recursion or a picture of a picture or something.
map filter sumlist
headingExerciseWrite a contract that takes in two integers that represent a minimum and a maximum.
Exercise: group forwarder. I, the king, send messages to the forwarder who copies them to all the recipients. Rather than just having kill switch, I have the ability to change group subscription.
Idea: [head ... tail]
|