轨道
/
Factor
Factor
/
练习
/
花园聚会
花园聚会

花园聚会

学习练习

简介

有时,一份状态需要在多个单词之间共享,而不必每次都让它在各自的栈效应中层层传递。Factor 的动态变量(用SYMBOL:声明)让每个单词都能访问存放在命名空间里的值。

SYMBOL:和SYMBOLS:

SYMBOL: name声明一个变量。SYMBOLS: a b c ;可以一次声明多个:

USING: namespaces ;

SYMBOLS: counter total ;

设置与获取

set-global和get-global(位于namespaces)操作的是全局命名空间:

set-global    ( value variable -- )
get-global    ( variable -- value )
change-global ( variable quot: ( old -- new ) -- )
0 counter set-global
counter get-global .         ! => 0
counter [ 10 + ] change-global
counter get-global .         ! => 10

对于模块级状态,你要的正是带 -global 后缀的那一套写法。

用with-variable做作用域绑定

动态变量还支持第二种作用域内的用法。set/get/change(不带 -global 后缀)读写的是当前的动态作用域,而with-variable(位于namespaces)会在一段引用执行期间创建一个全新的作用域:

with-variable ( value variable quot -- )
set           ( value variable -- )
get           ( variable -- value )
change        ( variable quot: ( old -- new ) -- )

在quot执行期间,变量会绑定到value;一旦quot返回,这个绑定就消失了。这样,外层的调用方就能“注入”一个值,让内层代码直接读取,而不必把它塞进每一个栈签名里。

SYMBOL: greeting

"Hello" greeting [
    greeting get .                ! => "Hello"
    "Howdy" greeting set
    greeting get .                ! => "Howdy"
    greeting [ "!" append ] change
    greeting get .                ! => "Howdy!"
] with-variable

greeting get-global .    ! => f   (the scoped binding is gone)

本练习自始至终都使用全局那一种,不过在你阅读别人的代码时,认出与之对应的set/get/change系列会很有用。

可变向量

因为动态变量保存的是引用,所以你可以把可变的集合放进一个动态变量里,并就地更新它:

SYMBOL: history

V{ } clone history set-global
"first event" history get-global push
"second event" history get-global push
history get-global .
! => V{ "first event" "second event" }

V{ … }是字面量向量。保存V{ }字面量之前一定要先clone:字面量在多次调用之间是共享的。

find:第一个匹配项

find(位于sequences)返回第一个让谓词为真的元素,以及它的下标:

find ( seq quot -- index elt/f )

如果没有任何元素匹配,两个值都是f。用nip丢掉下标,再用unless*,这就是标准的“缺失时取默认值”组合。

reject:丢掉匹配项

reject(同样位于sequences)与filter相反:它返回那些未通过谓词的元素。

说明

你的社区协会请你管理花园的地块登记。状态保存在两个动态变量中:

  • registrations,一个由 plot元组构成的向量,表示当前分配给某个人的地块。
  • next-id,下一次登记要使用的整数。

plot元组有两个槽位:

槽位 类型
id 整数
registered-to 字符串

1. 打开花园并列出其中的登记

定义 open-garden 来初始化这两个动态变量:把 registrations设为一个空向量,把 next-id设为 1。然后定义 list-registrations,让它返回当前的地块向量。

open-garden
list-registrations .
! => V{ }

2. 登记一个地块

定义 register:从栈上取出一个姓名,用下一个可用的 id 构造一个新的 plot,把它追加到 registrations向量中,把 next-id加一,然后返回这个新地块。

open-garden
"Emma Balan" register .
! => T{ plot { id 1 } { registered-to "Emma Balan" } }

list-registrations .
! => V{ T{ plot { id 1 } { registered-to "Emma Balan" } } }

地块 id 必须唯一,而且即使在释放之后也保持递增,next-id绝不能重复使用同一个值。

3. 释放一个地块

定义 release:接收一个 id,并从 registrations中删除匹配的条目。释放未知的 id 是无操作。

open-garden
"Emma" register drop
1 release
list-registrations .
! => V{ }

4. 获取一个已登记的地块

定义 get-registration:接收一个 id,返回匹配的地块;如果没有地块拥有这个 id,就返回符号 not-found。

open-garden
"Emma" register drop
1 get-registration .
! => T{ plot { id 1 } { registered-to "Emma" } }

7 get-registration .
! => not-found

5. 按姓名查找地块

定义 find-by-name:接收一个姓名,返回当前登记在该人名下的所有地块组成的向量。

open-garden
"Emma" register drop
"Bob" register drop
"Emma" register drop
"Emma" find-by-name .
! => V{ T{ plot { id 1 } { registered-to "Emma" } }
        T{ plot { id 3 } { registered-to "Emma" } } }
通过 GitHub 编辑 链接将在新窗口或新标签页中打开
Factor Exercism

准备好开始 花园聚会 了吗?

注册 Exercism,借助 47 个概念163 个练习 和真人导师指导,学习并掌握 Factor,全部免费。