【问题标题】:Is there a programmatic way to elaborate the 'half-winds' in raku?有没有一种程序化的方式来阐述 raku 中的“半风”?
【发布时间】:2021-04-27 09:12:34
【问题描述】:

我有这个工作:

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 -- 四分之一风的奖励积分!

【问题讨论】:

    标签: raku


    【解决方案1】:

    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]
    

    将四分之一风作为练习留给读者,但基本上这将使用四分之一风作为基础,并以类似的方式完成。

    【讨论】:

    • 感谢 lizmat - 由于易读性/可维护性,您的答案获胜。 FWIW 我需要将中频与 ords 一起重新拉回,因此您的最终解决方案可以减少到 3 行。我想我可以将切片编织到插值代码中......所以我将看看它与我的原始代码相比如何。
    【解决方案2】:

    假设我们手头有主风:

    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:
    

    内部展平可以用| slip 运算符替换:

    say flat $_ Z (|$_,.[0]).rotor(2 => -1).map(~*.sort(*.chars)) given @principals;
    

    这仍然比一开始列出它们要长,我可能会这样做,但无论如何尝试击败它很有趣......

    【讨论】:

    • 感谢 jnthn - 完全同意这件事的落脚点......我想你已经深入了解了我所问的问题,即“我为什么要以手写 x3 (或 4)" - 因为它非常算法化
    • 在第 3 行和第 4 行从底部调用 .words.elems 正确返回 16 个“单词”(即风)。然而,最后两行每行都将八个(非序数,非基数)半风作为两个“单词”返回,例如N NE ... E NE 等。因此在最后 2 行调用 .words.elems 会产生 24 个“单词”而不是前 2 行的 16 个“单词”。
    【解决方案3】:

    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)
    

    也许我可以通过解释上述内容获得一些奖励积分:

    • 前缀|"slips"右边的值;在四个象限中的每一个上使用它的最终效果是结果列表包含 32 个元素而不是 4 个。

    • .reverse 反转一个列表,对应于 NE/SW 和 SE/NW 之间的风点符号反转方式。

    • .map(...) 将函数或 lambda 应用于其调用者列表中的每个元素。

    • *.trans: 'NE'=&gt;'SE' 是一个 lambda,它将每个 N 字符音译为 S,并将每个 E 音译为 E。多余的E 用于第一个音译,因为它感觉比删除它更清晰一些;我也可以为第一象限添加*.trans: 'NE'=&gt;'NE',但感觉不太清晰。注意。我使用trans,因为它很简洁,但subst 总是可以做与trans 相同的事情,即使它需要更多的行,并且在Rakudo 2020.12 subst 中几乎所有的人都快5x-10x案例。

    • |quadrant\ .map... 中的\ 是一个“非空格”,一种向编译器指示它应该解析代码的方式,就好像斜杠和空格不存在一样。没有它,| 仅将 quadrant 作为其操作数。 unspace 使它将整个quadrant.map( *.trans: 'NE'=&gt;'SW') 表达式作为其操作数。 (在这个答案的原始版本中,我的代码是错误的,因为我在没有空格的情况下排列了 .map。)

    • .=unique 中的 .= 应用一个方法,然后将结果分配给它的调用者,在原地改变它。

    • unique 删除列表中重复的所有元素。如果没有它,就会重复四个基本点 (&lt;N E S W&gt;)。

    • .batch(8) 将这些点重新分成 8 个一组,对应于四个象限,准备好打印。

    奖励积分

    这个答案的其余部分只是我无耻加分的理由。

    来自您的问:

    四分之一风的奖励积分!

    来自丽兹的回答:

    把四分之一风留给读者作为练习

    “季风”32点

    正如 Liz 所说,一个包含她的四分之一风的简单超集解决方案只需要:

    以四分之一风为基础,并以类似的方式对其进行处理

    换句话说,做四分之一风奖金练习可能只需要编写琐碎、易于理解和合理维护的事情,即写出四分之一风列表中的 32 分,就像 Liz 写下 16 分一样。半风。 (但那样我就不会得到任何奖励积分。)

    将其扩展到令人困惑的半分(64 分)和四分之一分(128),对它们做的明智的事情也就是用手将它们写出来,并用 Unicode 小数字符完成,任何 PL支持 Unicode 源代码和“引用词”之类的功能(例如 Raku)变得微不足道。我敢肯定,如果您这样做,任何阅读您的代码的人都会感谢您保持简单。

    但我想找点乐子,坚持您的程序化主题,并尝试收集一些“奖励积分”。因此,我的代码一开始就是。


    我将创建更不必要的复杂隐秘繁琐的半点/四分之一点计算结构(支持别名和世界各地用于这些更精细指南针的不同约定)作为练习留给读者...

    【讨论】:

    • 感谢 raiph ... 很高兴看到另一种方法来构建它并利用数据集中的一些(接近)对称性 - 感谢您抽出时间......警告和所有跨度>
    • @raiph :“为这些更精细的指南针(及其惯例)创建(甚至更不必要的)复杂的、神秘的、繁琐的半点/四分之一点计算结构。”那是一些狡猾的辅音用法!
    • @jubilatious1 我确实喜欢巧妙地制作文字。 :) 当我第一次发表那行时,我写它的方式只有鹰眼的人会发现我所做的。但后来我改变了主意,把它编辑得更明显。我有一种奇怪的幽默感,我喜欢我的一些笑话相当明显,有些更狡猾,所以很少有人能抓住它,还有一些非常狡猾,尽管它们在众目睽睽之下,但它们很可能会被忽略每个人。我编辑了你引用的那一行以添加更多的文字,因为我觉得原文太神秘了。我只想说,我认为所有的迭代最终都会得到回报。?
    【解决方案4】:

    结束了这个......

    my @all-points = <N NNE NE ENE E ESE SE SSE S SSW SW WSW W WNW NW NNW>;
    my %pnt-count = %( cardinal => 4, ordinal  => 8, half-winds => 16 );
     
    my $iter = %pnt-count{$dec};
    my $step = 360 / $iter;
    my $rvc = ( $.value + $step/2 ) % 360;      #rotate value clockwise by half-step
    for 0..^$iter -> $i {
         my $port = $step * $i; 
         my $star = $step * ($i+1);
         if $port < $rvc <= $star {              #using slice to sample @all-points
             return @all-points[0,(16/$iter)...*][$i]
         }   
    }   
    

    【讨论】:

    • 估计那些四分之一风是为鸟类准备的
    • 这个(生成序数)怎么样? &gt; @all-points[(0..15).grep(not * % 2)].put 返回:N NE E SE S SW W NW
    【解决方案5】:

    我尝试从基本风 (NESW) 开始,但很快发现最好从 abcd 开始,因为关于 8 风罗盘(根据维基百科):

    -- 保加利亚语、加泰罗尼亚语、捷克语、丹麦语、荷兰语、英语、世界语、法语、加利西亚语、德语、希腊语、匈牙利语、伊多语、意大利语、日语 (通常)、马其顿语、挪威语(博克马尔语和尼诺斯克语)、波兰语、 葡萄牙语、罗曼什语、俄语、塞尔维亚语、克罗地亚语、西班牙语、瑞典语和 威尔士语表示北方或南方的部分在表示东方的部分之前 或西部。

    -- 在中文、盖尔语和日文中不太常见,表示东方或西方的部分在前。

    首先创建一个16点玫瑰:

    my @cardinal = <a b c d>;
    my @intercard = ((@cardinal Z @cardinal[1..*-1,0].flat)>>.join); #between cardinal
    my @pre_half  = ((@cardinal Z @intercard)>>.join).flat;
    my @post_half = (@intercard Z @cardinal[1..*-1,0].flat)>>.join;
    my @half = ((@cardinal Z @intercard).flat Z ([Z] @pre_half, @post_half).flat).flat;
    say @half;
    
    [a aab ab abb b bbc bc bcc c ccd cd cdd d dda da daa]
    

    现在转换为正确的字母 (NESW)。在 16 风罗盘中,重新排列末尾带有 &lt;same&gt; 字母的 3 个字母的风,将最后一个字母移到第一个字母,最后将风重新排序为英文顺序(北/南在东/西之前),与红衣主教间风放在最后,例如S-SE 不是 SE-S

    @half.=map(*.trans("abcd" => "NESW"));
    @half.=map({ S:g/ <((NE|ES|SW|WN)<same>(.))>$ /$1$0/});
    @half.=map(*.subst(:g, "ES","SE"));
    @half.=map(*.subst(:g, "WN","NW"));
    say @half;
    
    [N NNE NE ENE E ESE SE SSE S SSW SW WSW W WNW NW NNW]
    

    (...分成两个步骤,以防@p6steve 想为说中文、盖尔语和日语的人本地化他的代码)。

    注意:一旦您生成了 16 点(“半风”)罗盘玫瑰,就可以轻松找出 8 点(“序数”)罗盘玫瑰:

    @half[(0..15).grep(not * % 2)].put;
    
    N NE E SE S SW W NW
    

    HTH。

    https://en.m.wikipedia.org/wiki/Compass_rose#/media/File%3ABrosen_windrose.svg

    【讨论】:

    猜你喜欢
    • 2017-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-13
    • 1970-01-01
    • 2018-06-06
    • 1970-01-01
    • 2013-03-27
    相关资源
    最近更新 更多