SELECTの連携を今更ながら書いてみた

寺尾で答えてみた
https://teratail.com/questions/310078
なぜか面白くない。
提示されたコードは見難い。なので最初から自分で好きなように書いた方が楽。
汚いコードを指摘してあげるより、私はこれが楽だ。
本当の意味でのショートコーディングは奥が深い。
オブジェクト指向の書き方も奥が深い。


アンカータグの name 属性は無くなっていたのか!知らなかった。
ちょっとだけ賢くなった。

<!DOCTYPE html> 
<html lang="ja">
<meta charset="UTF-8">
<title>連携SELECT</title>
<style>
li > ol {
  margin: 0; padding: 0;
}
</style>
<body>
<ol>
  <li>第一選択肢
    <p>
      <select name="cat1">
        <option value="" selected>選択してください</option>
        <option value="0">a</option>
        <option value="1">b</option>
      </select>
    </p>

  <li>第二選択肢
    <ol>
      <li>
        <select name="cat2">
          <option value="">選択してください</option>
          <option value="0">a-a</option>
          <option value="1">a-b</option>
        </select>
      <li>
        <select name="cat2">
          <option value="">選択してください</option>
          <option value="2">b-a</option>
          <option value="3">b-b</option>
        </select>
    </ol>

  <li>第三選択肢
    <ol>
      <li>
        <select name="cat3">
          <option value="">選択してください</option>
          <option value="1">a-a-a</option>
          <option value="2">a-a-b</option>
        </select>
      <li>
        <select name="cat3">
          <option value="">選択してください</option>
          <option value="3">a-b-a</option>
          <option value="4">a-b-b</option>
        </select>
      <li>
        <select name="cat3">
          <option value="">選択してください</option>
          <option value="5">b-a-a</option>
          <option value="6">b-a-b</option>
        </select>
      <li>
        <select name="cat3">
          <option value="">選択してください</option>
          <option value="7">b-b-a</option>
          <option value="8">b-b-b</option>
        </select>
    </ol>
  
  <li id="cat4">結果
    <ol>
      <li>まだ全て選択されていません</li>
      <li><a href="exp.co.jp#a-a-a">a-a-a</a>
      <li><a href="exp.co.jp#a-a-b">a-a-b</a>
      <li><a href="exp.co.jp#a-b-a">a-b-a</a>
      <li><a href="exp.co.jp#a-b-a">a-b-b</a>
      <li><a href="exp.co.jp#b-a-a">b-a-a</a>
      <li><a href="exp.co.jp#b-a-b">b-a-b</a>
      <li><a href="exp.co.jp#b-b-a">b-b-a</a>
      <li><a href="exp.co.jp#a-a-a">b-b-b</a>
    </ol>
</ol>
<hr>
<ol>
  <li>第一選択肢
    <p>
      <select name="chk0">
        <option value="" selected>選択してください</option>
        <option value="0">a</option>
        <option value="1">b</option>
      </select>
    </p>

  <li>第二選択肢
    <ol>
      <li>
        <select name="chk1">
          <option value="">選択してください</option>
          <option value="http://www.xxx.co.jp#AA">a-a</option>
          <option value="http://www.xxx.co.jp#AB">a-b</option>
        </select>
      <li>
        <select name="chk1">
          <option value="">選択してください</option>
          <option value="http://www.xxx.co.jp#BA">b-a</option>
          <option value="http://www.xxx.co.jp#BB">b-b</option>
        </select>
    </ol>
</ol>

<script>
class A {
  constructor (name) {
    this.name = name;
    this.reg = new RegExp ('^'+ name + '(\\d+)$');
    this.next (document.querySelector (`*[name^="${name}"]`));
  }

  next (e) {
    let r; 
    if (r = this.reg.exec (e.name))
      this.nextStage (this.name + (Number (r[1]) + 1), Number (e.value || "-1"));
  }

  nextStage (name, no) {
    let
      es = document.querySelectorAll (`*[name="${name}"], #${name}>ol>li`),
      n = no < 0 ? 0: no;
    
    es.forEach ((e, i)=> {
      if ('select-one' === e.type) {
        e.disabled = no != i;
        e.selectedIndex = 0;
      }
      let li = e.closest ('li');
      if (li)
        li.style.display = n == i ? 'block': 'none';
    });

    if (es[0])
      this.next (es[0]);
  }

  handleEvent ({target: e}) {
    this.next (e);
  }
}

class B {
  constructor (...select) {
    this.select = select;
  }

  handleEvent ({target: e}) {
    if (this.select.includes (e) && e.value)
      if (confirm (e.value + 'に移動します'))
        location.href = e.value;
  }
}
//_____

document.addEventListener ('change', new A ('cat'), false);
document.addEventListener ('change', new A ('chk'), false);
document.addEventListener ('change', new B (...document.querySelectorAll ('select[name="chk1"]')), false);
</script>

PerlinNoise

class PerlinNoise {
  constructor (random = Math.random) {
    this.random   = random;
    this.gradient = new Map;
  }

  randomGradient (x) {
    if (! this.gradient.has (x))
      this.gradient.set (x, this.random () * 2 -1);
    return this.gradient.get (x);
  }

  noise (x) {
    let
      x0 = x |0, x1 = x0 + 1,
      r0 = x - x0, r1 = r0 - 1,
      u = r0 * this.randomGradient (x0),
      v = r1 * this.randomGradient (x1),
      sx = r0 * r0 * (3 - 2 * r0);

    return 2 * (u + sx * (v - u));
  }
}

PHP クラスの覚書

PHP クラスの覚書

<?php
/*
http://www.tohoho-web.com/php/class.html
*/

//class の使い方
// 1.定義

class A {
  //定数の定義
  const PI = 3.14;
  //プロパティの宣言
  public $a;//どこからでもアクセスできる
  protected $b;//クラス自身、継承したクラス、親クラスからアクセスできる
  private $c;//そのクラス自身だけ参照可

  //インスタンス化すると暗黙に最初に呼ばれる
  function __construct ($a, $b, $c) {
    $this->a = $a; // 4.
    $this->b = $b;
    $this->c = $c;
  }

  //以下メソッドの定義...
  public function dispA () {
    echo self::PI. "\n";//定数の参照
    echo $this-> a; 
  }

  //final 継承されたものからの上書きを禁止
  final public function dispB () {
    echo $this-> b;
  }

  //インスタンス化しない状態で呼び出す
  static function dispC () {
    echo "func dispC";
//    echo $this-> c;
  }
}

// クラスの継承
class B extends A { // 親となる A を継承することを意味する

  function __construct ($a, $b, $c) {
    parent::__construct ($a, $b, $c);//明示的に継承元を呼び出す必要あり
  }

  //オブジェクト消滅時に呼び出される
  function __destruct () {
    parent::__destruct ();//継承元へも呼び出す
  }

  public function dispA () {
    echo $this-> a; //継承もとのメソッドのオーバーライド dsipB は禁止状態
  }
}


$b = new B ('a', 'b', 'c');// 3.インスタンス化する

$_a = clone $b; // オブジェクトの複製
$_c = $_a; //参照元が同じなので同等
B::dispC ();

//______________________________
//抽象クラス (サブクラスDが必ず定義しなくてはならない抽象メソッドを定義します)

abstract class C {
  abstract public function dispA ();
  abstract public function dispB ();
  public function dispC () { echo $this-> a; }
}

class D extends C {
  public function dispA () { echo $this-> a; }
  public function dispB () { echo $this-> b; }
}

//new C (); #単独でこれはできない
new D (); #OK

//______________________________
//インタフェース (実装クラスが必ず定義しなくてはならないメソッドを定義します)
interface E {
  public function dispA ($arg);//変数も指定すること
  public function dispB ($arg, $arg2);//引数の名前は適当でよいが数は合わせる必要あり
  public function dispC ();
}

class F implements E {
  public function dispA ($arg) { echo $this-> a; }
  public function dispB ($arg0, $arg1) { echo $this-> b; }
  public function dispC () { echo $this-> c; }
}

new F ();

//______________________________
//タイプヒンティング
class A { }
class B {
  public function test (A $arg) {
    ;
  }
}

//______________________________
//オートローディング
spl_autoload_register (function ($class_name) {
  include("{$class_name}.php");//G.php が読み込まれる
});

$g = new G();
?>

MySQLPHP を経由して、JavaScript にデータを送るための

<?php

//_____________________________
//MySQL Driver PDO クラス生成
interface DBDriver { //このクラスを継承させること
  public function send ($sql, $prm, $type, $multi);
}


//_____
class MySQL implements DBDriver {
  
  const DBNAME = '**********';  //DB名前
  const HOST   = 'localhost'; //DBホスト名
  const USER   = 'root';      //DB使用ユーザー名
  const PASS   = '***********';  //DB暗証番号
  //MySQLの設定値(php5.3 では定数として配列が使えない)
  private static $opt = array (
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, //error時のthrowが有効になる
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, //fetch 時の取得方法
    PDO::ATTR_EMULATE_PREPARES=>false,
    PDO::ATTR_PERSISTENT=> true, //接続を維持する
    PDO::MYSQL_ATTR_USE_BUFFERED_QUERY=> true //バッファを利用する
  );
 
  //_____
  protected $pdo = null;//PDOの実態

  //_____
  //初回定義時
  function __construct ($name= self::DBNAME, $host= self::HOST, $user= self::USER, $pass= self::PASS, $opt= array ()) {
    try {
      $option = $opt + self::$opt; //$opt は上書きされない
      $db = sprintf ('mysql:dbname=%s; host=%s; charset=utf8mb4', $name, $host);
      $this->pdo = new PDO ($db, $user, $pass, $option);
    }
    catch (PDOException $err) {//MySQL でエラー発生
      if (DEBUG) {
        header ('Content-Type: text/plain; charset=UTF-8', true, 403);
        echo $err;
      }
      die ();
    }
    catch (Exception $ex) {
      die ();
    } 
  }
  //_____
  //送信
  public function send ($sql, $prm = null, $type = PDO::FETCH_ASSOC, $multi = true) {
    $PDO = $this->pdo;
    try {
      switch (true) {
      case (null === $prm) ://パラメータ無し
        switch ($type) {
        case 'count' : case 'COUNT' : case NULL: case FALSE :
          return $PDO-> exec ($sql);//戻り値は作用した行数
        case 'id' : case 'ID' :
          $hd = $PDO-> query ($sql);
          return $hd ? $PDO-> lastInsertId (): $hd; //結局失敗するとfalse
        default :
          $hd = $PDO-> query ($sql);
          return $multi 
            ? $hd-> fetchAll ($type)
            : $hd-> fetch ($type)
          ;
        }

      default ://パラメータ有り
        if (array_values ($prm) !== $prm)
          $prm = self::addColon ($prm);//連想配列ならコロンを付加

        $hd = $PDO-> prepare ($sql);
        $rst = $hd-> execute ($prm);
        //戻り値が無い場合
        switch ($type) {
          case 'id': case 'ID' : return $rst ? $PDO-> lastInsertId (): $rst;
          case 'count' : case 'COUNT' : return $rst ? $hd-> rowCount (): $rst;//失敗は false
          case NULL : case FALSE : return $rst;
        default:
          return $multi
            ? $hd-> fetchAll ($type)
            : $hd-> fetch ($type)
          ;
        }
      }
    }
    catch (PDOException $err) {
      if (DEBUG) {
        header ('Content-Type: text/plain; charset=UTF-8', true, 403);
        echo $sql."\n";
        echo Array_Deployment($prm)."\n";
        echo $err-> getMessage ()."\n";
      }
      die();
    }
  }


  //_____
  private function addColon ($ary) {
    $rst = array ();
    foreach ($ary as $key => $val) $rst[':'.$key] = $val;
    return $rst;
  }


}

//_____________________________

interface DBTableController {
//  public function __construct ($pdo, $tb_name, $id_name);
  public function select ($QUERY);
  public function update ($RECODE);
  public function insert ($RECODE);
  public function delete ($ID_LIST);
}

class TableRecorder implements DBTableController {
  private $pdo;
  private $tb_name;
  private $id_name;
  //_____

  function __construct (DBDriver $pdo, $tb_name, $id_name = 'ID') {
    $this->pdo = $pdo;
    $this->tb_name = $tb_name;
    $this->id_name = $id_name;
  }

  //_____



  public function select ($QUERY, $ARG= null, $LIMIT= 0, $OFFSET= 0) {
    $order = array ('%%TABLE%%', '%%ID%%');
    $replace = array ($this->tb_name, $this->id_name);
    $query = str_replace ($order, $replace, $QUERY);

    if ($LIMIT) {
      $query .= ' LIMIT '. strval ($LIMIT);
      //offsetの生成
      if ($OFFSET)
        $query .= ' OFFSET '. strval ($OFFSET);
    }
    $rst = $this->pdo->send ($query, $ARG);

    return array (
      'data'=> $rst,
      'query'=> $query,
      'arg'=> $ARG,
      'error'=> false === $rst
    );
  }

  public function length ($QUERY= 'SELECT COUNT(*) FROM %%TABLE%%', $ARG= null) {
    $rst = $this->pdo->send ($QUERY, $ARG, PDO::FETCH_COLUMN, false);
    return array (
      'length'=> $rst,
      'query'=> $QUERY,
      'arg'=> $ARG,
      'error'=> false === $rst
    );
  }


  public function getOffsetID ($ID, $OFFSET= 0) {
    $query = ($OFFSET > 0)
    ? 'SELECT %2$s FROM %1$s WHERE %2$s>=:ID ORDER BY %2$s LIMIT 1 OFFSET %3$d'
    : 'SELECT %2$s FROM %1$s WHERE %2$s<=:ID ORDER BY %2$s DESC LIMIT 1 OFFSET %3$d';
    $query = sprintf ($query, $this->tb_name, $this->id_name, abs ($OFFSET));

    $rst = $this->pdo->send ($query, array ('ID'=> $ID), PDO::FETCH_COLUMN, false);
    return array (
      'id'=> $rst,
      'query'=> $QUERY,
      'offset'=> abs ($ARG),
      'error'=> false === $rst
    );
  }

  //_____
  //レコードの更新
  public function update ($ID, $RECODE = array ()) {
    $query = sprintf (
      'UPDATE %1$s SET %4$s WHERE %2$s=%3$s',
      $this->tb_name,
      $this->id_name,
      $ID,
      self::convNameList ($RECODE) 
    );
    $rst = $this->pdo->send ($query, $RECODE, null);

    return array (
      'id'=> $RECODE[$this->id_name],
      'query'=> $query,
      'recode'=> $RECODE,
      'error'=> false === $rst
    );
  }


  //_____
  //レコードの挿入
  public function insert ($RECODE) {
    $names = array_keys ($RECODE);
    $query = sprintf (
      'INSERT INTO %s (%s) VALUES(:%s)',
      $this->tb_name,
      implode (',', $names),
      implode (',:', $names)
    );
    $id = count ($RECODE)
      ? $this->pdo->send ($query, $RECODE, 'id')
      : false
    ;
    return array (
      'id'=> $id,
      'query'=> $query,
      'error'=> false === $id
    );
  }


  //_____
  //指定されたIDを削除する
  public function delete ($ID_LIST = array ()) {
    $query = sprintf (
      'DELETE FROM %s WHERE %s IN (%s)',
      $this->tb_name, $this->id_name,
      implode (',', $ID_LIST)//IDを整数限定にしている?
    );
    $cnt = count ($ID_LIST)
      ? $this->pdo->send ($query, null, 'count')
      : false;
    return array (
      'count'=> $cnt,
      'query'=> $query,
      'id'=> $ID_LIST,
      'error'=> false === $cnt
    );
  }


//_____

  public function convNameList ($ary) {
    $rst = array ();
    foreach ($ary as $key => $v) $rst[] = sprintf ('%1$s=:%1$s', $key);
    return implode (',', $rst);
  }
}

/*
header ('Content-type: text/plain; charset=UTF-8');
header ('X-Content-Type-Options: nosniff');

$mysql = new MySQL();
$a = new TableRecorder ($mysql, "shain", "ID");
var_dump($a->select ('SELECT * FROM %%TABLE%% WHERE ?<%%ID%%', array (4)));

*/

?>

順列を求める

Javascriptで順列を求める

考えたコード

  function permutations (ary = [ ], r = ary.length) {

    function recursion (ary, r, A = []) {
      if (r == 0)
        return ary.map (a=> A.concat(a));

      let result = [];
      for (let i = 0; i < ary.length; i++) {
        let
          aaa = [...ary],
          a = aaa.splice (i,1);
        result = result.concat (recursion (aaa, r-1, A.concat(a)));
      }
      return result;
    }
    
    return recursion (ary, r -1);
  }


短く1行で

let  permutations=((f=(A,B=A.length,C=[])=>--B?A.reduce((a,b,c,[...d],_=d.splice(c,1))=>[...a,...f(d,B,[...C,b])],[]):A.map(a=>[...C,a]))=>f)();

console.log (permutation([0,1,2,3]));
  • 再帰処理が必要なため、呼び出す関数をfにする
  • [...d] は複写したものを使わないと元を破壊してしまう
  • _=d.splice(c,1)) で配列から1つ削除するが、ここでは配列を返してしまうので使われない。その代わり同等の b を使う。

classで焼き直し(書きかけ)

class Permutation {
  constructor (ary = [ ], r = ary.length) {
    this.ary = ary;
    this.r = r;
  }

  get n () {
    return this.ary.length;
  }

  get length () {
    const fx = this.constructor.factorial;
    let {n, r} = this;
    return fx (n) / fx (n - r);
  }

  *generator (n = 0) {
    for (let i = 0, I = this.length; i < I; i++) {
      yield this.pattern (i);
    }
  }

  pattern (n) {
    if (n < 0 || this.length < n)
      throw new Error ('値が範囲外です');
    /*書きかけ*/
    return n;
  }

  patternAll () {
    return this.constructor.pattern (this.ary, this.r);
  }

  static factorial (n = 1) {
    let r = 1;
    for (let i = n; i > 1; r *= i--) ;
    return r;
  }

  static pattern (ary = [ ], r = ary.length) {
    function recursion (ary, r, A = []) {
      if (r == 0) {
        return ary.map (a=> A.concat(a));
      }
      let result = [];
      for (let i = 0; i < ary.length; i++) {
        let aaa = [...ary], a = aaa.splice (i,1);
        result = result.concat (recursion (aaa, r-1, A.concat(a)));
      }
      return result;
    }
    return recursion (ary, r - 1);
  }
}

パーリンノイズ

 

パーリンノイズ

参考URL
https://mrl.cs.nyu.edu/~perlin/doc/oscar.html
https://github.com/a-i-to/perlin-noise-sample
GitHubから引用

        function PerlinNoise(random) {
          if (random) {
            this._random = random;
          } else {
            this._random = Math.random;
          }
          this._gradient = {};
        }
        
        PerlinNoise.prototype._randomGradient = function(x) {
          if (this._gradient[x] || this._gradient[x] === 0) {
            return this._gradient[x];
          }
          var g = this._random() * 2 - 1;
          this._gradient[x] = g;
          return g;
        };
        
        PerlinNoise.prototype.noise = function(x) {
          var x0 = Math.floor(x);
          var x1 = x0 + 1;
          var r0 = x - x0;
          var r1 = r0 - 1;
        
          var u = r0 * this._randomGradient(x0);
          var v = r1 * this._randomGradient(x1);
        
          var sx = r0 * r0 * (3 - 2 * r0);
        
          return 2 * (u + sx * (v - u));
        };
        
        this.PerlinNoise = PerlinNoise;
      
書き直したもの

        class PerlinNoise {
          constructor (random = Math.random) {
            this.random   = random;
            this.gradient = new Map;
          }
          
          randomGradient (x) {
            if (! this.gradient.has (x))
              this.gradient.set (x, this.random () * 2 -1);
            return this.gradient.get (x);
          }
        
          noise (x) {
            let
              x0 = x |0, x1 = x0 + 1,
              r0 = x - x0, r1 = r0 - 1,
              u = r0 * this.randomGradient (x0),
              v = r1 * this.randomGradient (x1),
              sx = r0 * r0 * (3 - 2 * r0);
        
            return 2 * (u + sx * (v - u));
          }
        }
      
CANVAS に描画してみる

        const
          PN = new PerlinNoise,
          canvas = CV0,
          ctx = canvas.getContext ('2d'),
          width = canvas.offsetWidth,
          height = canvas.offsetHeight / 2,
          centerY = height / 2,
          wave = 5,
          waveH = 100;
        
        canvas.width = width;
        ctx.beginPath ();
        ctx.moveTo (0, centerY);
        ctx.strokeStyle = 'rgba(255,0,0,.5)';
      
        for (let px = 0; px < width; px += 1) {
          let py = centerY - PN.noise (px * wave / width) * waveH + waveH / 2;
          ctx.lineTo (px, py);
        }
        ctx.stroke ();
      

JavaScriptで数値を漢数字に変換する

俺様仕様。
使おうと思ったらまともに動かなかった!なんで?
ということで書き直した。
動かしているうちに短くなってしまった
もう少し煮詰めれば、さらに短くなりそうなのだけれど…
今日は終了!

  function kansuji(a,t=0,r='',N=[...'〇一二三四五六七八九'],M=['',...'十百千'],K=['',...'万億兆京垓𥝱穣溝澗']){
    return(''+a).replace(/(\d+)/g,a=>{
      if(t||'0'==a)return[...a].map(n=>N[n]).join``;
      [...a].reverse().map((n,i)=>r=((n-=0)&&i%4&&n==1?'':N[n])+M[i%4]+(i%4?r:K[(i/4)|0]+r))
      return r;
    })
  }


前に書いて動かなかったやつ


  function kansuji(a,t=0,r='',N=[...'〇一二三四五六七八九'],M=['',...'十百千'],K=['',...'万億兆京垓𥝱穣溝澗']){
    return(''+a).replace(/(\d+)/g,a=>{
      if(t||'0'==a)return[...a].map(n=>N[n]).join``;
    [...a].reverse().map((n,i)=>r=(((n-=0)==1&&i%4||!n)?'': N[n])+M[i%4]+(i%4?r:K[(i/4)|0]+r))
      return r;
    })
  }


[
  "洋野町第123地割4567番地890123",
  0,
  10, 11, 12,
  100, 111, 123,
  1000, 1111, 1234,
  10000, 11111, 12345,
  100000, 111111, 123456,
  1000000, 1111111, 1234567,
  10000000, 11111111, 12345678,
  100000000, 111111111, 123456789,
  1000000000, 1111111111, 1234567890
].forEach (n=>{
  console.log (n, kansuji(n));
})

JavaScript class memo

JavaScript class memo

//ECMAScript2015: class

class A {
  constructor (a, b) {
    Object.assign (this, {a, b});
    this._val = null;//暗黙に内部変数
  }

  get value () {
    return this._val;
  }

  set value (val) {
    this._val = val;
  }

  get clone () {
    let {a, b} = this;
    return new this.constructor (a, b);
  }

  static tool (...arg) {
    return new this (...arg);
  }

  static toolsFunc () {
    const func = {
      cbFunc: function (a) { return a+a}
    }
    return func;
  }

  static option (key = null) {
    const option = {

    };
    return key ? option[key]: {...option};
  }
}

class B extends A {
  //もし constructor が無い場合、以下が暗黙に実行される
  // constructor(...args) { super(...args); }
  constructor (a, b, c) {
    super (a, b);//先に実行すること
    this.c = c;
  }

  test () {
    let a = super.clone;
  }

}