「ピーターからの問題」1から9までを使って分数の穴埋め算。解答のJavaScriptプログラム

Qiitaでこちらの問題をみたので、解いてみました。

アルゴリズム

考えることは大きく分けて以下の2つです。

  1. 1から9を1回ずつ使った組み合わせを列挙する
  2. 分数を計算して1かどうか確かめる

1から9を1回ずつ使った組み合わせを列挙する

こちらのアルゴリズムを説明する記事は作成中です。

コード内では、配列のインデックス0が一の位、1が十の位、として、 1から9を使った9桁の数字のうち、小さい方から数え上げるようにしています。

分数を計算して1かどうか確かめる

今回、分数の計算が必要に思えますが、式を以下のように変形すれば、分数を使わずとも解けることがわかります。

a/x + b/y + c/z = 1
// ↓
a*y*z + b*x*z + c*x*y = x*y*z

下準備

各マスに番号をふっておきます。配列のインデックスに対応します。

f:id:yucatio:20200214232720p:plain

1から9までの数字を格納している配列をnとすると、解く式は以下のように書けます。

n[0]/(n[1] * 10 + n[2]) + n[3]/(n[4] * 10 + n[5]) + n[6]/(n[7] * 10 + n[8]) = 1

コード

/**
 * 配列の順列を列挙するジェネレータ
 */
function* arrayEnumerator(numArr){
  yield [...numArr]
  while(true) {
    const changeIndex1 = numArr.findIndex((value, i , arr) => i >=1 && arr[i-1] > value)
    if (changeIndex1 < 0) {
      break
    }
    const changeNumber1 = numArr[changeIndex1]
    const newOrderArr = numArr.slice(0, changeIndex1)
    const changeNumber2 = Math.min(...newOrderArr.filter(value => value > changeNumber1))
    const changeIndex2 = newOrderArr.indexOf(changeNumber2)
    newOrderArr[changeIndex2] = changeNumber1
    numArr = [...newOrderArr.reverse(), changeNumber2, ...numArr.slice(changeIndex1 + 1)]
    yield [...numArr]
  }
}


const enumerator = arrayEnumerator([9, 8, 7, 6, 5, 4, 3, 2, 1])

for(let n of enumerator){
  // denominators(分母)
  const denomi = [10 * n[1] + n[2], 10 * n[4] + n[5], 10 * n[7] + n[8]]
  // 左辺
  const left = n[0] * denomi[1] * denomi[2] + n[3] * denomi[0] * denomi[2] + n[6] * denomi[0] * denomi[1]
  // 右辺
  const right = denomi.reduce((acc, cur) => acc * cur , 1)
  if (left === right) {
    console.log(`${n[0]}/${n[1]}${n[2]} + ${n[3]}/${n[4]}${n[5]} + ${n[6]}/${n[7]}${n[8]} equals to 1`)
    break
  }
}

あとがき

急いで書いたので、変数名が微妙になってしまいました。

JavaScriptでPython風のzip_longest関数を実装する

こちらの記事でPython風のzip関数を実装しました。

yucatio.hatenablog.com

今回はzip_longest関数を作成します。以下のように、各配列の同じインデックスの要素をまとめます。

const a1 = [1, 2, 3]
const a2 = ["Jan", "Feb", "Mar"]
const a3 = ["Garnet", "Amethyst", "Aquamarine"]

zip_longest(a1, a2, a3)
#=> [[1, "Jan""Garnet"], [2, "Feb""Amethyst"],  [3, "Mar""Aquamarine"]]

各配列の長さが異なる場合には、一番長い配列の長さになります。未定義値はundefinedになります。

const a1 = [1, 2, 3, 4]
const a2 = ["Jan", "Feb", "Mar", "Apr", "May"]
const a3 = ["Garnet", "Amethyst", "Aquamarine"]

zip_longest(a1, a2, a3)
#=> [[1, "Jan""Garnet"], [2, "Feb""Amethyst"],  [3, "Mar""Aquamarine"], [4, "Apr", undefined], [undefined, "May", undefined]]

zip_longest関数本体

zip_longest関数の実装です。

const zip_longest = (...arrays) => {
  const length = Math.max(...(arrays.map(arr => arr.length)))
  return new Array(length).fill().map((_, i) => arrays.map(arr => arr[i]))
}

コードの解説

ほとんどzip関数の解説と同じです。

まず、関数の定義の部分を解説します。レスト構文を使用して、引数全てをarraysに格納します。

const zip_longest = (...arrays) => {

}

例えば、

const a1 = [1, 2, 3, 4]
const a2 = ["Jan", "Feb", "Mar", "Apr", "May"]
const a3 = ["Garnet", "Amethyst", "Aquamarine"]

zip_longest(a1, a2, a3)

と呼び出したとき、arrays

[
  [1, 2, 3, 4],
  ["Jan", "Feb", "Mar", "Apr", "May"],
  ["Garnet", "Amethyst", "Aquamarine"]
]

です。以下、引数にこの配列を渡したときの動作を説明します。

zip_longest関数では、一番長い配列の長さに合わせるので、まず一番長い配列の長さを求めます。

まず、各配列の長さを、求めます。

const zip_longest = (...arrays) => {
  arrays.map(arr => arr.length)
  #=> [4, 5, 3]
}

この中の最大値は、Math.maxとスプレッド演算子を使用して、以下のように書けます。

const zip_longest = (...arrays) => {
  const length = Math.max(...(arrays.map(arr => arr.length)))
  #=> const length = Math.max(...[4, 5, 3])
  #=> const length = Math.max(4, 5, 3)
  #=> const length = 5
}

5回繰り返すので、new Array(length).fill().map((_, i) => i)の構文を使用します。

const zip_longest = (...arrays) => {
  const length = Math.max(...(arrays.map(arr => arr.length)))
  new Array(length).fill().map((_, i) => i))
  #=> [0, 1, 2, 3, 4]
}

fillをはさむ理由については、こちらの記事をご覧ください。

yucatio.hatenablog.com

各配列のi番目の要素は、

arrays.map(arr => arr[i])

で取得することができます。よくわからない場合は、iではなく、012など具体的な数字で考えるとよいです。例えば、各配列の0番目の要素は、

arrays.map(arr => arr[0])
#=> [1, "Jan""Garnet"]

です。

これをmapに渡す関数の戻り値にします。

JavaScriptの場合、配列の長さより大きいインデックスを指定した場合はundefinedになります。

const zip_longest = (...arrays) => {
  const length = Math.max(...(arrays.map(arr => arr.length)))
  return new Array(length).fill().map((_, i) => arrays.map(arr => arr[i]))
  #=> [[1, "Jan""Garnet"], [2, "Feb""Amethyst"],  [3, "Mar""Aquamarine"], [4, "Apr", undefined], [undefined, "May", undefined]]
}

以上でzip_longest関数の完成です。

補足的な話題はzip関数の方の記事に記載していますので、あわせてご覧ください。

yucatio.hatenablog.com

JavaScriptでPython風のzip関数を実装する

JavaScriptzip関数がなかったので実装してみました。以下のように、各配列の同じインデックスの要素をまとめます。

const a1 = [1, 2, 3]
const a2 = ["Jan", "Feb", "Mar"]
const a3 = ["Garnet", "Amethyst", "Aquamarine"]

zip(a1, a2, a3)
#=> [[1, "Jan""Garnet"], [2, "Feb""Amethyst"],  [3, "Mar""Aquamarine"]]

各配列の長さが異なる場合には、一番短い配列の長さに切り詰められます。

const a1 = [1, 2, 3, 4]
const a2 = ["Jan", "Feb", "Mar", "Apr", "May"]
const a3 = ["Garnet", "Amethyst", "Aquamarine"]

zip(a1, a2, a3)
#=> [[1, "Jan""Garnet"], [2, "Feb""Amethyst"],  [3, "Mar""Aquamarine"]]

zip関数

zip関数の実装です。

const zip = (...arrays) => {
  const length = Math.min(...(arrays.map(arr => arr.length)))
  return new Array(length).fill().map((_, i) => arrays.map(arr => arr[i]))
}

コードの解説

まず、関数の定義の部分を解説します。レスト構文を使用して、引数全てをarraysに格納します。

const zip = (...arrays) => {

}

例えば、

const a1 = [1, 2, 3, 4]
const a2 = ["Jan", "Feb", "Mar", "Apr", "May"]
const a3 = ["Garnet", "Amethyst", "Aquamarine"]

zip(a1, a2, a3)

と呼び出したとき、arrays

[
  [1, 2, 3, 4],
  ["Jan", "Feb", "Mar", "Apr", "May"],
  ["Garnet", "Amethyst", "Aquamarine"]
]

です。以下、引数にこの配列を渡したときの動作を説明します。

次に、各配列の長さを求めます。

const zip = (...arrays) => {
  arrays.map(arr => arr.length)
  #=> [4, 5, 3]
}

この中の最小値は、Math.minとスプレッド演算子を使用して、以下のように書けます。

const zip = (...arrays) => {
  const length = Math.min(...(arrays.map(arr => arr.length)))
  #=> const length = Math.min(...[4, 5, 3])
  #=> const length = Math.min(4, 5, 3)
  #=> const length = 3
}

3回繰り返すので、new Array(length).fill().map((_, i) => i)の構文を使用します。

const zip = (...arrays) => {
  const length = Math.min(...(arrays.map(arr => arr.length)))
  new Array(length).fill().map((_, i) => i))
  #=> [0, 1, 2]
}

fillをはさむ理由については、こちらの記事をご覧ください。

yucatio.hatenablog.com

各配列のi番目の要素は、

arrays.map(arr => arr[i])

で取得することができます。よくわからない場合は、iではなく、0、1、2など具体的な数字で考えるとよいです。例えば、各配列の0番目の要素は、

arrays.map(arr => arr[0])
#=> [1, "Jan""Garnet"]

です。

これをmapに渡す関数の戻り値にします。

const zip = (...arrays) => {
  const length = Math.min(...(arrays.map(arr => arr.length)))
  return new Array(length).fill().map((_, i) => arrays.map(arr => arr[i]))
  #=> [[1, "Jan""Garnet"], [2, "Feb""Amethyst"],  [3, "Mar""Aquamarine"]]
}

以上でzip関数の完成です。

zip_longest

Pythonzip_longestにあたる関数はMath.minの部分をMath.maxにするだけです。 こちらの記事に詳しい動作を載せています。

yucatio.hatenablog.com

イテレータの使用

Pythonではイテレータを使用して配列の要素にアクセスしています。list[i]のようにインデックスを指定してアクセスするよりも、イテレータの方が速いことが期待されるからでしょう。 (参考: 組み込み関数 — Python 3.8.1 ドキュメント )

JavaScriptにもイテレータがあるので そちらを利用しようかと思いましたが、あまり慣れている書き方ではないので今回は見送りました。 ( 参考:Array.prototype.values() - JavaScript | MDN)

rubyのzipメソッド

rubyにもzipメソッドがあり、似た動きをするのですが、こちらはArrayクラスのインスタンスメソッドです。こちらはレシーバの要素数が作成後の配列の長さになります。

Array#zip (Ruby 2.7.0 リファレンスマニュアル)

[1,2,3].zip([4,5,6], [7,8,9])
# => [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

プログラミングで初学者のときにつまづいたこと

プログラミングでつまづいてきたことというブログ記事を見たので、私も書いてみます。

satoru-takeuchi.hatenablog.com

mizchi.hatenablog.com

自己紹介

記事をリンクしたお2人と違って大したプログラマではないですが、システムエンジニアとして働いて10年経ってみて今の仕事が向いていると思っているところです。

大学でC言語Javaを習って、仕事はWeb系で主にJavaを使用しています。

以下、つまづいたことです。だいたい古いのから新しいものになっています。

代入文

a = 1
b = 2
c = a + b

でcが3になるのは分かったのだが、

a = a + 1

これを見て、"左右が等しくない。"と混乱し、そこで思考が止まってしまった。 半年後くらいに代入文という言葉を覚え、上記は"a + 1を計算した結果をaに入れる"だということが分かった (が慣れるまでにはもう少しかかった)。

アルゴリズム

プログラムそれ自体よりもアルゴリズムでつまづく(大学の授業で習った。言語はC言語。)

バブルソートわからない。わかってもコードに落とし込めない。2重ループが難しすぎる。

こんなに便利な世の中なのに、並び替えをするのにいちいち自前でプログラミングをしなければいけないなんて、どうなっているのだと思った。 (色々と認識が間違っていた)

Java

オブジェクト、インスタンス、クラス

用語の説明を何度読んでも理解ができない。他にもとにかく馴染みのないカタカナ語が多すぎて嫌になった。

あるとき、C言語の構造体に関数を足したのがクラスだと気づきそこから理解が進んだ。が、C言語を習っていなかったらと思うと震える。

Javaオブジェクト指向 (継承、ポリモーフィズムカプセル化)

惑星クラスを拡張したのが、水星クラス、金星クラス、土星クラスとか、 犬クラスだったら"ワン"と鳴いて、猫クラスだったら"ミャー"と鳴くとか、何に使うかがわからなかった。 値の隠蔽(カプセル化)が重要という割に、setterで値を操作できているではないか、と思った。

理解できるようになったのは、Javaデザインパターンを読んでから。


ちなみに"オブジェクト指向とは何か"はこちらの記事でWindows95を開発した中島聡さんの言葉を引用しています。

yucatio.hatenablog.com

インターフェース

インターフェースも何に使うか長年の疑問だった。実装がないとは何の役にも立たないのではないか。

確かこの本だったと思うけど、"例えば、自動販売機、外側に見える部分がちゃんと仕様通りに動いていれば、中に人が入っていて、手でジュースを出してもよい。自動販売機の、外から見える部分がインターフェース、中の機械とかが実装"、といったことがイラスト付きで書いてあって、その図がシュールで印象に残った。この例からなんとなく理解し始めたように思う。


その後、"仕様と実装を分けて考える"ということができるようになってきたのでインターフェースの使い所もはっきりしてきた。

Rubyのmap関数

array.map {|i| i * 2 }

で、なぜ各配列の値が2倍されるのか分からなかった。Javaしかほぼやってこなかったので、変換後の配列が定義されていないことが不思議だった。

JavaのMapとRubyのmapは全くの別物ということに思い当たったのと(JavaのMapはRubyのhash)、 とりあえずJavaでの記法(for文を使うもの)と対比させて覚えた。

プログラミング以外でのつまづきも多い

リンク先での記事でも「オブジェクト」「スタック」「ヒープ」が多義語で分かりにくかったとの記載があったが、 自分の場合は「ドメイン」「インスタンス」が多義語で混乱した。がこれはプログラミングではないもの(プログラミングの周辺知識ではあるが)も含まれているので機会があれば書きたい。

最近はプログラミング言語それ自体というより、周辺のツールで使用される概念や用語の理解に苦労する。 最近だとDockerの用語の理解でつまづいた。

あとがき

書きながら、プログラム始めたばかりのころ 全然コードが動かなくて吐きそうになったのを思い出しました。

とりあえず私から言えることは、Javaは最初に学ぶような言語ではないということです。

eclipseのJavadocの警告設定の各設定値の意味【後編】

Javadocの記述が不完全なときに警告を出そうとして、どのような設定があるか調べたメモです。

チーム開発でJavadocをどの程度書くかどうか、意識を統一するのにこの記事をご活用いただければと思います。

前編の記事

yucatio.hatenablog.com

未指定の Javadoc タグ

"未指定の Javadoc タグ"をONにすると、メソッドに定義されていてJavadocに記載されていないパラメータや例外などがある場合に警告を出します。

f:id:yucatio:20200113175535p:plain

メンバーの可視性を次のように設定

設定した可視性以上のJavadocのみ、"未指定の Javadoc タグ"の警告を出します。

オーバーライドしたメソッドの実装を無視

Javaでは、オーバーライドしたメソッドのJavadocは、子クラスの内容で親クラスの内容を上書きした内容となります。 以下の例では、子クラスでdescriptionと変数xの記述を上書きし、他のparamとreturnについては、親クラスの記述が引き継がれています。

f:id:yucatio:20200113180300p:plain

"オーバーライドしたメソッドの実装を無視"をOFFにすると、オーバーライドしたメソッドであっても、未指定の Javadoc タグがある場合には警告を出します。

f:id:yucatio:20200113180008p:plain

ただし、@Overrideアノテーションが付いている場合、警告は出ません。

f:id:yucatio:20200113180021p:plain

詳しい仕様と仕様が策定された経緯につきましてはこちらをご覧ください。

6.15 complains about missing @return tag in a overridden method · Issue #2869 · checkstyle/checkstyle · GitHub

メソッド型パラメータを無視

メソッド型パラメータとは、 ジェネリックメソッドにおいて、メソッドの戻り値の型の直前に書かれた<T>などの型の仮型パラメータのことです。 (参考: Java ジェネリクス(クラス、メソッドを定義する) - Qiita )

"メソッド型パラメータを無視"がONのときは、この<T>のためのJavadoc(@param <T>)がなくても警告を出しません。

OFFにすると、警告を出します↓

f:id:yucatio:20200113195747p:plain

未指定の Javadoc コメント

"未指定の Javadoc コメント"をONにすると、Javadocが記載されていない場合に警告を出します。

"メンバーの可視性を次のように設定"を"Protected"にした時の表示です↓

f:id:yucatio:20200113215408p:plain

メンバーの可視性を次のように設定

設定した可視性以上のJavadocのみ、警告を出します。

オーバーライドしたメソッドの実装を無視

"オーバーライドしたメソッドの実装を無視"をONにすると、オーバーライドしたメソッドにJavadocが無くても警告を出しません。

オーバーライドしたメソッドは、Javaでは親クラスのJavadocが使用されるため、指定しなくても問題ないことが多いかもしれません。

"オーバーライドしたメソッドの実装を無視"をOFFにすると、オーバーライドしたメソッドにも警告を出します。

f:id:yucatio:20200113220027p:plain

"未指定の Javadoc タグ > オーバーライドしたメソッドの実装を無視" の設定とは違い、こちらは@Overrideアノテーションのあるなしでの挙動の違いはありません。

オーバーライドしたメソッドに親と同じ内容のJavadocを書ときは、{@inheritDoc}タグを使うと便利です。

  /**
   * {@inheritDoc}
   */
  @Override
  public int calcA(int x, int y, int z) {
    return x + y + z + 1;
  }

参考: オーバーライドしたメソッドのJavadoc - 気付いたとき、気が向いたとき。by ykhr

環境

あとがき

"未指定の Javadoc タグ > オーバーライドしたメソッドの実装を無視" の設定が自分の想定と違った動作をしたので意外と苦戦しました。

参考サイト

eclipseのJavadocの警告設定の各設定値の意味【前編】

Javadocの記述が不完全なときに警告を出そうとして、どのような設定があるか調べたメモです。

チーム開発で、Javadocをどの程度書くかどうか、意識を統一するのにこの記事をご活用いただければと思います。

Javadocの警告設定

Javadocの書き方がよくないときに警告を出す設定の場所は、 設定 > Java > コンパイラー > Javadoc です。

f:id:yucatio:20200112222657p:plain

Javadocコメントを処理

"Javadocコメントを処理"をONにすると、Javadocとメソッドが連携されます。 変数名を選択したときにJavadocのparamの変数名もハイライトされます。

ONのとき↓

f:id:yucatio:20200112223145p:plain

OFFのとき↓

f:id:yucatio:20200112223156p:plain

また、ONにすると、変数のリネーム時にJavadocのparamの変数も同時に変更されます。

ONのとき↓

f:id:yucatio:20200112223411p:plain

OFFのとき↓

f:id:yucatio:20200112223421p:plain

誤った形式の Javadoc コメント

"誤った形式の Javadoc コメント"をONにすると、波かっこが閉じていないときなどに警告を出します。

f:id:yucatio:20200112223630p:plain

メンバーの可視性を次のように設定

設定した可視性以上のJavadocのみ、警告を出します。

可視性をProtectedにしたときに、PublicとProtedtedメソッドのにのみ警告が出て、パッケージとPrivateのメソッドに警告は出ません↓(タグ引数の検証をONにしたときの警告)

f:id:yucatio:20200112224059p:plain

"タグ引数の検証"をONにすると、@param, @throws, @exceptions, @see, @linkに 指定されたクラスやメソッドが存在しないときに警告を出します。

min1という引数はメソッドに存在しないため、警告が出ます↓

f:id:yucatio:20200113142031p:plain

f:id:yucatio:20200113141532p:plain

Nowhere.nomthodという存在しないクラスとメソッドを参照しているので警告が出ます↓

f:id:yucatio:20200112224211p:plain

不可視参照をレポート

"不可視参照をレポート"をONにすると、 不可視なメソッドなどを参照をしたときに警告を出します。不可視な参照とは、例えば他のクラスのprivateなメソッドや定数です。

他のクラス(SamplePrivateクラス)のprivateな定数(DEFAULT_NAME)を参照しているので、警告が出ます↓

f:id:yucatio:20200112224444p:plain

使用すべきでない参照をレポート

"使用すべきでない参照をレポート"をONにすると、非推奨(Deprecated)となったメソッドや変数を参照したときに警告します。

f:id:yucatio:20200112224653p:plain

タグ記述の欠落

"タグ記述の欠落"の設定では、 @param 変数名@returnなどのタグのあとの記述が無いときに警告を出します。

@return タグの検証ではその名の通り、@returnタグの後に何も書かれていなければ警告を出します。その他のタグの後には何も書かれていなくても警告を出しません。

f:id:yucatio:20200112225611p:plain

全ての標準タグを検証にすると、全ての標準タグにおいて、タグの記述がない場合に警告を出します。標準タグとは javadoc - Java API ドキュメントジェネレータ に記載されているタグです。

f:id:yucatio:20200112225624p:plain

後編

長くなったので続きます。

yucatio.hatenablog.com

環境

参考サイト

eclipseの警告表示の重大度レベルごとの挙動

前回の記事で、eclipseの警告レベルをどれにするか迷ったので、各重大度レベルでの挙動を確認しました。

yucatio.hatenablog.com

eclipseの警告表示の設定

eclipseを使用していると、黄色の波線と警告マーク(3角形の中に!)が表示されたことがあるでしょう。 これらの設定は、 設定 > Java > コンパイラー > エラー/警告 で変更可能です。

f:id:yucatio:20200110081256p:plain

エラー、警告、情報、無視の違い

エラー/警告の設定では、eclipseが問題を検出したときにの重大度を設定できます。

重大度レベルごとのeclipseの挙動です。

重大度レベル 表示
エラー 問題がある箇所に赤の点線が引かれ、該当する行及びファイル名にバツ(×)印がつきます。さらに、プログラムの実行時に"必要なプロジェクトでのエラー"のダイアログが表示されます。("続行"を押すと実行できます。)
警告 問題がある箇所に黄色の点線が引かれ、該当する行及びファイル名にエクスクラメーションマーク(!)がつきます。実行時にダイアログは出ません。
情報 問題がある箇所に青の波線が引かれ、該当する行及びファイル名に"i"マークがつきます。実行時にダイアログは出ません。
無視 何も表示しません。

エラー

エラーの時の表示です。

f:id:yucatio:20200110081144p:plain

実行時にダイアログが表示されます↓

f:id:yucatio:20200110081158p:plain

警告

警告の時の表示です。

f:id:yucatio:20200110081127p:plain

情報

情報の時の表示です。

f:id:yucatio:20200110081113p:plain

無視

無視の時は何も表示されません。

f:id:yucatio:20200110081101p:plain

エラー、警告、情報の一覧を表示する

現在のワークスペースの警告の一覧を表示するには、ウィンドウ > ビューの表示 > その他 > 一般 > マーカーを開きます。

f:id:yucatio:20200110081050p:plain

"マーカー"のビューに現在のワークスペース内に存在するエラーや警告が表示されます。各項目をクリックすると該当箇所へジャンプすることができます。

f:id:yucatio:20200110081025p:plain

以上、eclipseの警告設定と各重大度レベルごとの挙動でした。

環境