LoginSignup
2
0

Codewarsで欲を満たす

Posted at

はじめに

RUNTEQの中間試験が楽しく、久しぶりにCodewarsをやりたくなったのでやっていきます。

どの言語でやろうかな・・・
JSはホームグラウンド。楽しくできるはず。
Rubyはちょうどやったところ。もういっちょやるのはあり。
Haskellも中間試験やりながらよぎったんだよな・・・きれいな再帰を書きたい・・・

やってみた問題(JS)

今回はJS 6kyuの問題を解きました。

You are given an array(list) strarr of strings and an integer k. Your task is to return the first longest string consisting of k consecutive strings taken in the array.

Examples:

strarr = ["tree", "foling", "trashy", "blue", "abcdef", "uvwxyz"], k = 2

Concatenate the consecutive strings of strarr by 2, we get:

treefoling   (length 10)  concatenation of strarr[0] and strarr[1]
folingtrashy ("      12)  concatenation of strarr[1] and strarr[2]
trashyblue   ("      10)  concatenation of strarr[2] and strarr[3]
blueabcdef   ("      10)  concatenation of strarr[3] and strarr[4]
abcdefuvwxyz ("      12)  concatenation of strarr[4] and strarr[5]

Two strings are the longest: "folingtrashy" and "abcdefuvwxyz".
The first that came is "folingtrashy" so 
longest_consec(strarr, 2) should return "folingtrashy".

In the same way:
longest_consec(["zone", "abigail", "theta", "form", "libe", "zas", "theta", "abigail"], 2) --> "abigailtheta"

n being the length of the string array, if n = 0 or k > n or k <= 0 return "" (return Nothing in Elm, "nothing" in Erlang).

Note

consecutive strings : follow one after another without an interruption

解答
function longestConsec(strarr, k) {
  const n = strarr.length
  if(n === 0 || k > n || k <= 0) {
    return ""
  }
  
  let max = ""
  for(let i = 0; i < n+1-k; i++) {
    const joined = strarr.slice(i, i+k).join("")
    if(max.length < joined.length) {max = joined}
  }
  return max

簡単に解説すると、まずエラーになるケースは先に排除していますね。

メインのロジック部分は、slice()を使用し、配列の特定の要素から特定の要素までを取得→つなげて、今までの最大のものと比較しています。
最初は作成した文字列を新しい配列に入れていましたが、メモリ消費が多かったので変更しました。

sliceというメソッドを完全に忘れてて、連想配列の書き方も怪しかったですw
久しぶりにやってみるとやっぱいいですね。

ただ、これだけじゃ再帰欲が満たされないので、Haskellの過去の解答を見てみましょう。

過去やった問題(Haskell)

module FindMaximumAndMinimumValuesOfAList (minimum, maximum) where

import Prelude hiding (minimum, maximum)

minimum :: [Int] -> Int
minimum [x] = x
minimum (x:xs) = min x (minimum xs)

maximum :: [Int] -> Int
maximum [x] = x
maximum (x:xs) = max x (maximum xs)

美しい・・・無駄な部分が一切ありません。
今回の問題は、「配列の中の最小・最大を返す関数を自作しろ」「2つの引数を取り、どちらが小さい・大きいかを返すmin / max はあるよ」というものですね。

  1. minimum[int] (intの配列)を受け取り、単一のintを返す
  2. 配列の中身が一つのとき ([x])、それを返す (= x)
    (これは再帰の終了条件になります。)
  3. minimumは受け取った配列を先頭とそれ以外に分割し ((x:xs))、先頭xとそれ以外の中で最小のものminimum xsのどちらがより小さいか比較する(min

つまり、
min x (minimum xs) = min x (min x' (minimum xs')) = min x (min x' (min x'' (minimum x'')))`・・・
と、配列の最後の1つになるまで式を展開します。
このように、関数の中で関数自身を呼び出すことを再帰と言います。

具体例を出すと、配列が[1, 5, 2, 4]だったとき、
minimum (x:xs) = min x (minimum xs)
= minimum (1:1以外) = min 1 (minimum 1以外)
= min 1 (min 5 (minimum 1と5以外))
= min 1 (min 5 (min 2 minimum 1と5と2以外))
この時、1と5と2以外というのが4しかないので、minimum 1と5と2以外minimum [4]となります。[x]ならxを返す、と設定していましたので、minimum [4]4を返します。
= min 1 (min 5 (min 2 4))
このような式ができあがります。
後は順番に計算していくだけですね。min 2 42を返します。
= min 1 (min 5 2)
= min 1 2
= 1
[1, 5, 2, 4]の配列から、単一の最小値1を取得できました!

改めて式を振り返りましょう。

minimum :: [Int] -> Int
minimum [x] = x
minimum (x:xs) = min x (minimum xs)

何をやってるか見てわかる、超シンプルなのにあれだけの計算をしてる。
また、配列がどんなに大きくても(理論上)行き詰まることはない。
美しいですね・・・

ちなみに、問題を見たい方はこちらです。

8kyu(最も簡単なレベル)の問題です。
Haskell以外でも書けるので、codewarsやったことない方はやってみてください!

おわりに

満足!🤗

次は何を書こうかしら・・・Codewarsで欲を満たす

2
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0