はじめに
RUNTEQの中間試験が楽しく、久しぶりにCodewarsをやりたくなったのでやっていきます。
どの言語でやろうかな・・・
JSはホームグラウンド。楽しくできるはず。
Rubyはちょうどやったところ。もういっちょやるのはあり。
Haskellも中間試験やりながらよぎったんだよな・・・きれいな再帰を書きたい・・・
やってみた問題(JS)
今回はJS 6kyuの問題を解きました。
You are given an array(list)
strarr
of strings and an integerk
. 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
ork > n
ork <= 0
return "" (returnNothing
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
はあるよ」というものですね。
-
minimum
は[int]
(intの配列)を受け取り、単一のint
を返す - 配列の中身が一つのとき (
[x]
)、それを返す (= x
)
(これは再帰の終了条件になります。) -
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 4
は2
を返します。
= 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で欲を満たす