有没有一种程序化的方式来阐述raku中的“半风”?
我有这个工作:
my %pnt = %( cardinal => <N E S W>,
ordinal => <N NE E SE S SW W NW>,
half-winds => <N NNE NE ENE E ESE SE SSE S SSW SW WSW W WNW NW NNW>,
);
我认为有可能以编程方式创建半风阵列。
然而,我可怜的尝试似乎更加冗长:
my @cards = <N E S W>;
my @odds = <NE SE SW NW>;
my @ords = ( @cards Z @odds ).flat;
my @mids = ( ( @cards Z~ @odds ) Z ( @cards.rotate(1) Z~ @odds ) ).flat;
my @halfs = ( @ords Z @mids ).flat;
say @cards; #[N E S W]
say @ords; #[N NE E SE S SW W NW]
say @mids; #[NNE ENE ESE SSE SSW WSW WNW NNW]
say @halfs; #[N NNE NE ENE E ESE SE SSE S SSW SW WSW W WNW NW NNW]
有没有更好/更简洁的替代方案?
即。https://en.wikipedia.org/wiki/Points_of_the_compass
- 四分之一风的奖励积分!
回答
FWIW,在这种情况下,我会考虑代码的可读性,我认为您的原始代码是您可以获得的最易读的代码。
但是如果你想获得算法,我会@halfs使用一个序列来使用数组切片:
my @halfs = <N NNE NE ENE E ESE SE SSE S SSW SW WSW W WNW NW NNW>;
my @mids = @halfs[1,3...*];
my @ords = @halfs[0,2...*];
my @cards = @halfs[0,4...*];
say @cards; #[N E S W]
say @ords; #[N NE E SE S SW W NW]
say @mids; #[NNE ENE ESE SSE SSW WSW WNW NNW]
say @halfs; #[N NNE NE ENE E ESE SE SSE S SSW SW WSW W WNW NW NNW]
将四分之一风留给读者作为练习,但基本上那将使用四分之一风作为基础,并以类似的方式解决这个问题。
回答
假设我们手头有主风:
my @principals = <N NE E SE S SW W NW>;
然后我们可以遵循维基百科的定义:
每个半风的名称是通过将主风的名称组合到任一侧来构造的
这意味着我们要以重叠的成对获取主风,这rotor可以做得相当巧妙:
say @principals.rotor(2 => -1);
这给了我们:
((N NE) (NE E) (E SE) (SE S) (S SW) (SW W) (W NW))
遗憾的是,它有一个问题,因为它错过了(NW, N). 好的,我们可以N再次包含,以牺牲一点美感为代价:
say @principals[*,0].flat.rotor(2 => -1)
给予:
((N NE) (NE E) (E SE) (SE S) (S SW) (SW W) (W NW) (NW N))
如果我们加入他们:
say @principals[*,0].flat.rotor(2 => -1).map(*.join)
我们得到:
(NNE NEE ESE SES SSW SWW WNW NWN)
这还不正确,因为文章接下来要说的是:
主风先来,间风次之
主要的风是一个字符的,可以通过排序来修复:
say @principals[*,0].flat.rotor(2 => -1).map(*.sort(*.chars).join)
最终看起来是正确的:
(NNE ENE ESE SSE SSW WSW WNW NNW)
除了半风位于主风之间,这可以通过压缩它们并压平结果来解决:
say flat @principals Z @principals[*,0].flat.rotor(2 => -1).map(*.sort(*.chars).join)
最后给我们:
(N NNE NE ENE E ESE SE SSE S SSW SW WSW W WNW NW NNW)
如果我们希望它更短,重复提到的@principals可以用一个given:
say flat $_ Z .[*,0].flat.rotor(2 => -1).map(*.sort(*.chars).join) given @principals
该.join可刚刚成为列表字串:
say flat $_ Z .[*,0].flat.rotor(2 => -1).map(~*.sort(*.chars)) given @principals:
并且可以使用|滑动运算符替换内部扁平化:
say flat $_ Z (|$_,.[0]).rotor(2 => -1).map(~*.sort(*.chars)) given @principals;
这仍然比首先列出它们要长,这就是我可能会做的,但无论如何尝试击败它很有趣......
回答
TL;DR无缘无故,这个 nanswer只是为了加分。
my quadrant = <N NbE NNE NEbN NE NEbE ENE EbN E>;
my @quarts = |quadrant,
|quadrant.reverse.map( *.trans: 'NE'=>'SE'),
|quadrant .map( *.trans: 'NE'=>'SW'),
|quadrant.reverse.map( *.trans: 'NE'=>'NW');
@quarts .=unique .batch(8) .map: *.say;
# (N NbE NNE NEbN NE NEbE ENE EbN)
# (E EbS ESE SEbE SE SEbS SSE SbE)
# (S SbW SSW SWbS SW SWbW WSW WbS)
# (W WbN WNW NWbW NW NWbN NNW NbW)
也许我得到了一些奖励积分来解释上述内容:
-
前缀
|“滑动”其右侧的值;在四个象限中的每个象限上使用它的净效果是结果列表包含 32 个元素而不是 4 个。 -
.reverse反转列表,对应于风点符号在 NE/SW 和 SE/NW 之间反转的方式。 -
.map(...)将函数或 lambda 应用于其调用者列表中的每个元素。 -
*.trans: 'NE'=>'SE'是一个将每个N字符音译为S和每个E到的 lambdaE。第E一次音译有多余的部分,因为它感觉比丢弃它更清晰;我也可以*.trans: 'NE'=>'NE'为第一象限添加,但感觉不太清楚。注意。我使用trans它是因为它简洁,但subst总是可以做同样的事情trans,即使需要更多的行,而且在 Rakudo 2020.12subst中,几乎在所有情况下都快 5 到 10 倍。 -
该
中|quadrant .map...是一个“unspace”的方式来指示编译器,它应该解析代码,如果斜线和空白是不存在。没有它,|就quadrant如同它的操作数一样。unspace 使其将整个quadrant.map( *.trans: 'NE'=>'SW')表达式作为其操作数。(在这个答案的原始版本中,我的代码是错误的,因为我在.map没有 unspace的情况下排列了。) -
将
.=在.=unique适用的方法,然后分配结果返回给调用者的,在地方变异它。 -
unique删除列表中重复的任何元素。没有它,四个基点 (<N E S W>)就会重复。 -
.batch(8)将这些点分成 8 个一组,对应于准备漂亮打印的四个象限。
奖励积分
这个答案的其余部分只是我无耻的奖励积分清除的理由。
从你的问题:
四分之一风的奖励积分!
来自莉兹的回答:
将四分之一风留给读者作为练习
“四分之一风”的32分
正如 Liz 所指出的,一个包含她的风和四分之一风的简单超集解决方案只需要:
使用四分之一风作为基础,并以类似的方式解决它
换句话说,做四分之一风奖金练习可以只需要写一些简单、容易理解和合理维护的东西,即写出四分之一风列表中的 32 分,就像 Liz 对半风的 16 分所做的那样。(但那样我就得不到加分了。)
将此扩展到令人困惑的半点(64 点)和四分之一点(128),为它们做的明智之举也是用手将它们写出来,并使用 Unicode 分数字符完成,任何支持 Unicode 的 PL源代码和“引用词”之类的功能(例如 Raku)变得微不足道。我敢肯定,如果您这样做,任何阅读您代码的人都会感谢您保持简单。
但我想找点乐子,坚持你的程序化主题,并尝试寻找一些“奖励积分”。因此,我一开始的代码。
我将创建更多不必要的复杂的、复杂的、繁琐的半点/四分之一点计算结构(完全支持别名和世界各地为这些更精细的圆规使用的不同约定)作为读者练习......