週刊Challen 088タスクシュノーラ2::(Perl、RAKUなど)



TASK #2 › Spiral Matrix
提出されます:モハメッドsアンワール
あなたは正の整数のm×n行列を与えられます.
リストとしてスパイラル行列を印刷するスクリプトを書く.
Example 1:

Input:
    [ 1, 2, 3 ]
    [ 4, 5, 6 ]
    [ 7, 8, 9 ]
Ouput:
    [ 1, 2, 3, 6, 9, 8, 7, 4, 5 ]
Example 2:

Input:
    [  1,  2,  3,  4 ]
    [  5,  6,  7,  8 ]
    [  9, 10, 11, 12 ]
    [ 13, 14, 15, 16 ]
Output:
    [ 1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10 ]

命令的アプローチ(PerlGolangCommon-lisp)
私が共通のLISPソリューションを終えたとき、私はこのアプローチがよりよいと思います.
マトリックスからスパイラル配列を取得するには、行列の周りのセルを読み取る方法を知る必要があります.
のようにスパイラルの方法を通過してセル内を取得するオフセットがないように行きましょう.

北セグメント
公平に言えば、これだけは言うことはない
なぜなら、行(配列)を読むのが自然な方法だからです
これはPerlコードです.
push @spiralArray, @mat[0] # reading top(first) of the matrix

東セグメント
また、列を読むのに自然な方法を持っていますが、行列の一番上の一番右のセルは既に読みます.それで、我々は細胞をスキップする必要があります.
最初に、それが命令的なアプローチのための良い言語であるので、ゴランコードを見ましょう.
...
if num_rows > 1 {
    for r := 1; r <= row_end; r++ {
        sarr = append(sarr, mat[r][col_end])
    }
}
...
一般的なLispコードは
...
   (let ((east-side (loop for r from (1+ o) upto row-end
                           collect (aref mat r col-end))))
     (when (not (null east-side))
       (nconc sarray east-side)
...
コモンLispはループのための素晴らしいマクロを持っています.
しかし、Perlでいくつかの異なる方法を適用しました.
...
    push @spiral_array,
      map { $_->[$col_end] } @mat[ $o + 1 .. $row_end ];
...
はい.わかってる.それは奇妙に見えます.私はこの期間の地図に夢中です.しかし、ラクでは、我々ははるかに良い書くことができます.以下のコードは擬似コードですが、大丈夫です.
 @spiral-array.append( @matrix[ 1 .. $row_end; $col_end] )
これは本当にクールだと思う.便利で簡単!

南セグメント
一番下の右側のセルが既にスキャンされているので、南の方は少し異なります.我々は再びそれらを省略する必要があります.また、セルの発生順序を逆転させる必要がある
...
    # south
    if ( $num_cols > 1 ) {
        push @spiral_array,
          @{$mat[$row_end]}  # bottom
            [ reverse 1 .. ($col_end -1) ]; # reverse order as well
...

西セグメント
最後の1つは、他のセグメントのために最短のメンバーが欲しているので、彼らはすべて彼らができるものを得る.
...
    # west
      if ( $num_rows > 2 ) {
          push @spiral_array,
            map { $_->[$o] } reverse @mat[ $o + 1 .. $row_end - 1 ];
      }
...

タマネギの中に入る方法🧅 マトリックス?
私は、マトリックスの1つの層を読んで、行列の中に入るたびに、成長するオフセット変数を適用しました.また、行の数と列の数もオフセット変数と共に減少しました.
...
    for ( my $o = 0; $num_rows > 0 && $num_cols > 0; ++$o ) {
        my ($row_end, $col_end) = map { $o + $_ -1 } $num_rows, $num_cols;
    ... snip ...
       # go inner matrix
        $num_rows -= 2;
        $num_cols -= 2;
    }
...
また、オフセット変数とNumCount行とNumCount COLORで動作するようにコードを変更する必要があります!それで、私は"getspiralarrayfrommatrixref () "という関数の完全なコードを残します
sub getSpiralArrayFromMatrixRef ($) {
    my @mat = @{$_[0]}; # copy

    my $num_rows = scalar @mat;
    my $num_cols = scalar @{$mat[0]};

    my @spiral_array;
    for ( my $o = 0; $num_rows > 0 && $num_cols > 0; ++$o ) {
        my ($row_end, $col_end) = map { $o + $_ -1 } $num_rows, $num_cols;

        # north
        push @spiral_array, @{$mat[$o]}[$o .. $col_end];
        # east
        if ( $num_rows > 1 ) {
            push @spiral_array,
              map { $_->[$col_end] } @mat[ $o + 1 .. $row_end ];
            # south
            if ( $num_cols > 1 ) {
                push @spiral_array,
                  @{$mat[$row_end]}[ reverse $o .. ($col_end -1) ];
                # west
                if ( $num_rows > 2 ) {
                    push @spiral_array,
                      map { $_->[$o] } reverse @mat[ $o + 1 .. $row_end - 1 ];
                }
            }
        }
        # go inner matrix
        $num_rows -= 2;
        $num_cols -= 2;
    }

    @spiral_array
}

ビット機能的アプローチ(RakuHaskell)
ラクでは、マトリックスの外側を時計回りに読んで、内部のマトリックスでそれを返すピールオフと呼ばれる機能を作りました
sub peel-off( @a ) {
    my ( $re, $ce ) = @a.end, @a[0].end;

    my @inside  = @a[ 1 .. $re.pred; 1 .. $ce.pred ] // Empty;
    @inside .= rotor($ce.pred).map(*.Array) if @inside; 
    my @outside = @a[ 0; * ];                           # outside of top

    for ( [ 1 .. $re;     $ce         ],                # outside of right
          [ $re;          $ce.pred,
                          $ce.pred.
                           pred ... 0 ],                # outside of bottom
          [ $re.pred,
            $re.pred.
             pred ... 1;   0         ]  )               # outside of left
    -> ( $rr, $cr ) {
        last unless all( $rr.elems.so, $cr.elems.so );  # out of range
        @outside.append: @a[$rr[*]; $cr[*]];
    }

    @inside, @outside
}
RAKUはマトリックスの範囲でかなりよく働きます、それで、私は各々のセグメントのために行とコラム範囲を繰り返すために、forループを使います.そして、基本的にこれは私の最初の解決でした.そして、私は繰り返しを使用して内部に行く必要があります.一方ループ
my ( $in, $out );
repeat {
    ( $in, $out ) = peel-off( @mat );
    @spiral.append: |$out;
    @mat = |$in;

} while ( @mat[0] andthen { .elems > 0} );
多くのデバッグなしで期待通りに動作します.
Haskellでは、私はunfoldrと呼ばれるより高い順序関数を使用できました
getSpiralAarray :: [[String]] -> [String]
getSpiralAarray mat =
  foldr1 (++)
  ( unfoldr (\m ->
                 case m of
                   Nothing -> Nothing
                   Just m' -> case (getInnerMatrix m') of
                                Left  _   -> Just (outOf m', Nothing)
                                Right m'' -> Just (outOf m', Just m''))
      (Just mat) )
  where outOf m = readAroundMatrixCW m
そして、私はマトリックスの外側を読むための機能と別のものを得る
getInnerMatrix :: [[String]] -> Either String [[String]]
getInnerMatrix m
  | length m <= 1 = Left "Too Short In Row"
  | otherwise = mapCutColumns [] getShorterInRows
    where
      mapCutColumns acc [] = Right acc
      mapCutColumns acc (r:rs) =
        case getShorterInCols r of
          Nothing -> Left "Too Short In Column"
          Just r' -> mapCutColumns (acc ++ [r']) rs
      getShorterInRows = (init.tail) m -- cut top and bottom
      getShorterInCols row =
        if (length row) <= 1 then Nothing
        else Just $ (init.tail) row

(init.tail) composition works like @a[1..$#a] in Perl
this code is not efficient probably, but I think it is easier to debug. Debugging in Haskell is not easy, IMHO. Especially it is not when debugging a pure function.


そして外で読むこと.
readAroundMatrixCW :: [[String]] -> [String]
readAroundMatrixCW [] = []
readAroundMatrixCW m =
  foldr1 (++) $
  getNorth
  : case findEast of
      Nothing -> []
      Just  e -> e
        : case findSouth of
            Nothing -> []
            Just  s -> s
              : case findWest of
                  Nothing -> []
                  Just  w -> [w]
  where
... snip ...
私はブログの目的のための場所の部分をスキップしました.主な違いは、ブランチの戻り値を指定する必要があることです.そして、私はそれにまだ満足です.この種の練習はPerlでも関数を作成するときに大いに役立ちます.

Elm Solution
不要なデータを生成する機会があることに気づいた後、ELM(基本的な関数プログラミング言語)で別のタイプのソリューションを作りました.私はデバッグするのが難しいと告白しました.ELMのリストの要素にアクセスするのは便利ではないからです.おそらく、私はデータと一緒に各行番号や列番号をzipする必要がありますが、私はただ、ドロップ、テイクと“ヘッド”の組成を使用します.
以下のコードはエルムの西セグメントを読むためのものです.
...
        west  = {-if L.isEmpty south then []
                else-} -- already checked previously
                if nr <= 2 then []
                else mat
                    |> L.drop (o+1)
                    |> L.take (nr-2)
                    |> L.reverse
                    |> L.filterMap (L.drop o >> L.head)
...
しかし、私は本当にそれが機能的なプログラミング言語!我々は下から上にもう読む必要はありません!
>>Haskellと同じ構成演算子ですが、矢印の方向が示すように順序を適用すると右から左になります.それでは普通の作文法があります.です.
わかりました.それは今週のすべてです.
是非忘れてはいけない!
🐪PWC🦋
休暇をとってチャレンジします.もっと大切なことをする必要があります.
そして、来年戻ってきます!👋
と言う“メリークリスマス”、“と”、“新年おめでとうございます!”
$Myoungjin_JEON=@@=qw^rekcaH lreP rehtonA tsuJ^;$|++;{$i=$like=pop@@;unshift@@,$i;$~=18-length$i;print"\r[","~"x abs,(scalar reverse$i),"~"x($~-abs),"]"and select$good,$day,$mate,1/$~for 0..$~,-$~+1..-1;redo}