OmniAICreator/Qiita-1.07M huggingface dataset:
kintone テーブルの変更イベントで行位置を求める

kintone テーブルの変更イベントには、テーブル行位置の情報が無いので、テーブル行位置を求めてみます。

# 概要

テーブルの変更イベント時の情報には、テーブル行のオブジェクトがあるので、テーブルの各行とオブジェクト比較を行うと、テーブル行位置を求めることができます。

例：
table[i] === event.changes.row

cybozu developer network [レコード編集画面でフィールドの値を変更したときのイベント](https://cybozu.dev/ja/kintone/docs/js-api/events/edit/edit-change-event/)
>changes.row	オブジェクト	値を変更したテーブルの行データ
行を追加した場合：追加した行のデータ
行を削除した場合：「null」
テーブル外のフィールドを変更した場合：「null」

# 確認用アプリ

テーブル内項目を変更したら、テーブル行位置に行位置をセット

![2023-12-29_13h08_55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/aed3e47b-8a45-cc10-5bc0-ea0a67b5541c.png)

# 操作例

変更した項目の行位置が、テーブル行位置と pos にセットされます。

![2023-12-29_13h16_35.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/d4fbf0a7-a37f-8eec-ad03-4535bbda0bb3.gif)

# 確認用 JavaScript

```.js
(function() {
  'use strict';
  const evs = [
    'app.record.create.change.テーブル',
    'app.record.create.change.文字列T',
    'app.record.create.change.数値T',
    'app.record.create.change.ユーザー選択T',
    'app.record.edit.change.テーブル',
    'app.record.edit.change.文字列T',
    'app.record.edit.change.数値T',
    'app.record.edit.change.ユーザー選択T'
    ]
  kintone.events.on(evs, function(event) {
    console.log('t1', event);
    let record = event.record;
    let table = record.テーブル.value;
    
    for (let i=0; i<table.length; i++) {
      if (table[i] === event.changes.row) {
        record.テーブル行.value = i;
        event.changes.row.value.pos.value = i;
        return event;
      }
    }
    
    record.テーブル行.value = '';
    return event;
  });
})();
```
SwallowのLlamaTokenizerFastに常用漢字「𠮟」「塡」「剝」「頰」を追加するには

[12月21日の記事](https://qiita.com/KoichiYasuoka/items/60c44df1740efb38e04e)の続きだが、Swallowのトークナイザは[常用漢字2136字](https://www.bunka.go.jp/kokugo_nihongo/sisaku/joho/joho/kijun/naikaku/kanji/)のうち51字をサポートしておらず、これら51字はバイトフォールバックしてしまう。ただ、第3水準漢字の「𠮟」「塡」「剝」「頰」については、代わりに「叱」「填」「剥」「頬」で事前学習されているようだ。[Swallow-7b-hf](https://huggingface.co/tokyotech-llm/Swallow-7b-hf)で確かめてみよう。

```
>>> from transformers import LlamaTokenizerFast
>>> tkz=LlamaTokenizerFast.from_pretrained("tokyotech-llm/Swallow-7b-hf")
>>> print(tkz.convert_ids_to_tokens(sum(tkz(["𠮟責","頰","剝離","装塡"])["input_ids"],[])))
['
', '▁', '<0xF0>', '<0xA0>', '<0xAE>', '<0x9F>', '責', '
', '▁', '<0xE9>', '<0xA0>', '<0xB0>', '
', '▁', '<0xE5>', '<0x89>', '<0x9D>', '離', '
', '▁', '装', '<0xE5>', '<0xA1>', '<0xA1>']
>>> print(tkz.convert_ids_to_tokens(sum(tkz(["叱責","頬","剥離","装填"])["input_ids"],[])))
['
', '▁', '叱', '責', '
', '▁', '頬', '
', '▁', '剥', '離', '
', '▁', '装', '填']
```

それならば、入力中の「𠮟」「塡」「剝」「頰」を、それぞれ「叱」「填」「剥」「頬」に[`Replace`](https://huggingface.co/docs/tokenizers/api/normalizers#tokenizers.normalizers.Replace)するよう、トークナイザを書き換えてしまった方がうれしい気がする。とりあえず、現状の`normalizer`がどうなっているか見てみよう。

```
>>> import json
>>> d=json.loads(tkz.backend_tokenizer.to_str())
>>> print(d["normalizer"])
{'type': 'Sequence', 'normalizers': [{'type': 'Prepend', 'prepend': '▁'}, {'type': 'Replace', 'pattern': {'String': ' '}, 'content': '▁'}]}
```

[`Sequence`](https://huggingface.co/docs/tokenizers/api/normalizers#tokenizers.normalizers.Sequence)の中身は、メタスペースの`Prepend`と、空白からメタスペースへの`Replace`なので、「𠮟」「塡」「剝」「頰」の`Replace`を`Sequence`に追加すればよさそうだ。

```
>>> from tokenizers.normalizers import Sequence,Prepend,Replace
>>> tkz.backend_tokenizer.normalizer=Sequence([Prepend("\u2581"),Replace(" ","\u2581"),Replace("𠮟","叱"),Replace("頰","頬"),Replace("剝","剥"),Replace("塡","填")])
>>> print(tkz.convert_ids_to_tokens(sum(tkz(["𠮟責","頰","剝離","装塡"])["input_ids"],[])))
['
', '▁', '叱', '責', '
', '▁', '頬', '
', '▁', '剥', '離', '
', '▁', '装', '填']
```

よし、うまくいった。mySwallow-7b-hfに保存しよう。

```
>>> tkz.save_pretrained("mySwallow-7b-hf")
('mySwallow-7b-hf/tokenizer_config.json', 'mySwallow-7b-hf/special_tokens_map.json', 'mySwallow-7b-hf/tokenizer.model', 'mySwallow-7b-hf/added_tokens.json', 'mySwallow-7b-hf/tokenizer.json')
```

ちなみに、モデル自体も同じディレクトリに保存しておくと、`pipeline`とかの読み込みで便利だったりするが、27GBもあるのでハードディスクと相談してほしい。

```
>>> from transformers import LlamaForCausalLM
>>> mdl=LlamaForCausalLM.from_pretrained("tokyotech-llm/Swallow-7b-hf")
>>> mdl.save_pretrained("mySwallow-7b-hf")
```

ただし、この`Replace`をやってしまうと、たとえば「[頬と頰はどちらが子供の名づけに使えますか？](https://dictionary.sanseido-publ.co.jp/column/%e7%ac%ac107%e5%9b%9e-%e3%80%8c%e9%a0%ac%e3%80%8d%e3%81%a8%e3%80%8c%e9%a0%b0%e3%80%8d)」という質問には、絶対に答えられなくなってしまう。その点に注意して`Replace`してほしい。
チームみんなでAWS資格取ったぞい

# チームみんなでAWS資格取ったぞい

チームのみんなで中級者向けのAWS資格であるSAA（Solutions Architect - Associate）を取得しました。この記事では、私たちが合格するために取り組んだことや工夫した点について、合格体験記として書いていきます。

## 対象読者
この記事は以下のような方々を対象にしています：
- 駆け出しエンジニア
- プログラミング初学者
- SAA資格を取得したい方
- チームメンバーと一緒に資格を取得したい方
- チームで資格を取得したい方

## きっかけ
私たちのチームはAWSを頻繁に利用する機会が多くあり、会社全体でもAWSを推奨されていました。そのため、いつかはAWSの資格を取得したいと思っていました。しかし、なかなかきっかけがなく時間が経っていました。そこで、自分だけではなくチーム全員で資格取得に挑戦しようという企画を立てました。

## 取り組んだこと
- 毎日30分のモクモク勉強会
- 実践を想定した定期的な模擬試験
- 実際の試験への申し込み
- 発起人の私が一足先に合格
- 学習の進め方の共有

## 詳細
### モクモク勉強会
　モクモク勉強会では、毎日30分間を設け、チーム全員で集まります。
 仕事の合間に強制的に時間を確保することで、毎日少しずつ学習を進める習慣を作りました。
 チームメンバーはミュートで参加しながらも、ミーティングに参加していることからやらなければという気持ちになりました。
 
　また、会社としてもこの取り組みを推奨しており、業務時間内に30分を確保することができました。ただ単に自己学習するだけでなく、実務との関連性を意識しながら、チームメンバーと学んだAWSサービスの実務での活用方法について話し合いました。
 
 ※なお、弊社では期間限定のキャンペーンにより、資格取得に補助金が出るので、モチベーションも高めることができました。

### 定期的な模擬試験
　私たちは定期的に本番試験を想定した模擬試験を行いました。
試験時間は約90分でミーティングを設け、模擬試験を実施しました。模擬試験はバトル形式で行い、メンバー同士が競い合うことで楽しみながら学習を進めることができました。

　また、自分がどの程度の位置に立っているかを知ることができ、合格ラインに対してどれくらいの距離があるかを把握しました。模擬試験を通じてモチベーションを高めることも重要であり、取得する資格に対して意欲を持ち続けることができました。さらに、模擬試験で苦手な単元を見つけ出し、復習する機会としました。
試験後それぞれ報告している様子。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628336/2f2d2815-8ba9-ccb8-e7ea-677eea3a530f.png)


### 本試験への申し込み
実際の本試験への申し込みは、非常に効果的な手段であると感じました。試験申し込み料も15,000円前後という費用がかかるため、一気に緊張感が高まりました。模擬試験での成績が一定水準に達したら、各メンバーに本試験の申し込みを促しました。

### 自分が先に挑む
企画を立てた私自身が最初に合格することで、メンバー全員に合格の可能性を示しました。

### 学習の進め方の共有
チーム内での学習の進め方を共有しました。これにより、一貫性を持って学習を進めることができ、効率的に合格に近づくことができました。なお、チームとしての合格を目指すことが本筋から逸れてしまうため、ご了承ください。

## SAAの学習方法
以下はSAAの学習方法についての詳細です：

### 使用教材
- [Udemyの動画](https://works-hijp.udemy.com/course/aws-uoyc/learn/lecture/23061820#overview)
- [Udemyのテスト](https://works-hijp.udemy.com/course/aws-knan/learn/quiz/4699792/results?expanded=1090104638#overview)
- [ping-t](https://mondai.ping-t.com/users/sign_in)

### 学習の進め方
　まず、Udemyの動画を倍速で視聴し、試験範囲や頻出ポイントを押さえながら主要なサービスを学習しました。
 その後、ping-tで単元別の問題を解き、正解の理由や他の選択肢をなぜ選ばなかったのかを詳しく明記した解説を読みました。
 
　また、参考ボタンを押すと、サービス全体の概要が説明されるため、非常に便利でした。特に、ping-tは無料で利用できる上に、スマートフォンからも学習しやすい点がおすすめです。移動時間や隙間時間などを活用して学習を進めることができました。
 
 　さらに頻繁に間違える問題や理解が難しい暗記系の問題については、単語帳アプリで学習し記憶を定着させました。
  そしてイメージがつきにくい部分など、理解が進まないものについては、AWSのコンソールで実際に操作してみることも行いました。
  
  模擬試験の結果を参考にして苦手と考えられる単元の復習も行いました。
  
  合格するためには70％以上の正解率が求められるため、試験を解いて仕上げをする流れにしました。なお、私の場合は760点でギリセーフでした。

## DVAの学習方法
SAA合格後、暇があったので開発者向けのDVA（Developer - Associate）の資格も受けてみました。

### 使用教材
- [ポケットスタディ AWS認定 デベロッパーアソシエイト](https://www.amazon.co.jp/dp/4798063401?psc=1&ref=ppx_yo2ov_dt_b_product_details)

### 学習の進め方
SAAの学習で押さえた基礎知識を活かし、ポケットスタディのテキストを読みながら章末問題を解いていきました。わからなかった箇所については、重点的に再度学習しました。
DVAの試験範囲はSAAよりも狭く設定されているため、細かい使い方や設定について問われることが多いです。

SAAに比べて試験合格までの学習時間が限られており、私自身は10時間程度しか取れませんでした。試験までの時間がないため、748点というギリギリの合格点でした。

## まとめ
全メンバーが合格できたことは非常に喜ばしいです。
また、この資格取得の過程で、チームの絆が深まる効果もあったと思います。資格取得はゴールではありませんが、実務に活かせるスキルとして受験することもおすすめです。

最後に、勢いで取り組むことが重要であり、後回しにするとなかなか取り組むことが難しくなるため、挑戦するタイミングを逃さないようにしましょう！

また、アウトプットすることで知識が定着するため、多くの問題を解いたり、他の人に教えることも重要です。さらに、単語帳を活用することで情報の定着も助けられます。

以上が私たちの合格体験記のまとめです。
GHQとPECOを用いてGITリポジトリ管理を簡単にする

クローンしてきた、Gitリポジトリがどこのディレクトリにあるかわからん😕
やっと探したけど、この無駄な時間を返してケロと思ったことがある人向けの記事です！！

## 概要
こいつらを使えばGitリポジトリの管理が圧倒的に改善されます

https://github.com/x-motemen/ghq

https://github.com/peco/peco


## 使い方(多分5分でできる)
ghqをインストール
```
$ brew install ghq
```

ghqで管理するrootディレクトリを`~/src`に設定する
```
$ git config --global ghq.root '~src'
```

pecoの設定ファイルを編集する。
```
$vim ~/.zshrc
```
↓以下を追加する
```
## ghqとの連携。ctrl + ]にバインド。
function peco-src () {
  local selected_dir=$(ghq list -p | peco --prompt="repositories >" --query "$LBUFFER")
  if [ -n "$selected_dir" ]; then
    BUFFER="cd ${selected_dir}"
    zle accept-line
  fi
  zle clear-screen
}
zle -N peco-src
bindkey '^]' peco-src
```

`.zshrc`を再度読み込む。
```
source ~/.zshrc
```

以下のコマンドを実行
```
ctrl + ]
```
↓以下のように一覧とディレクトリの移動が手早くできるようになります。
![Qiita (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2698022/bf0b71a4-1751-bc80-6d77-12776e30863e.gif)

◽️参考(ほぼこの人の丸ぱくり。ごめんなさい)

https://zenn.dev/oreo2990/articles/13c80cf34a95af
【振り返り】2023年

# 目的

私の「2023年の振り返り」と「2024年の抱負」をKPTフレームワークを使用して、行います。自身の意識を改め、修行を開始したのが8月頃なので少ないですが、いい機会なので行います。

# Keep(振り返り)

- 修行の必要性に気づけたこと
8月頃に自身のキャリアについて考え、何かを変えるために行動できた。
- 自分の時間を作れたこと
平日は7時過ぎに会社に行き、始業時間まで修行。終業後は体力・時間に余力があれば帰りに図書館へ行き20~21時くらいまで修行。
- 資格取得
今年は2つの資格取得ができた。どちらも難易度がとても高いというわけではないが、修行の成果が出てうれしかった。
  1. QC検定3級
  2. LPIC-1
- 毎月読書1冊以上読めたこと
修行の気分転換で何冊か読めたので良かった。読み方は自分に合う方法を模索中。

# Probrem

- 体調管理
9月頭に体調を崩してしまった。心身の健康第一優先。

# Try(抱負)

- 修行継続
  - 資格取得
  - アルゴリズム修行
  - 読書
- **AtCoderのレートを緑へ**
現在(2023/12/23)、灰色(88点)なので緑レート(1200点以上)を目指す！

# 最後に
記事を最後まで読んでくださった方々、ありがとうございました。  
来年も引き続き修行を継続するよう努めていきます。
投稿アプリで本人のみ編集削除を許可しよう

オンラインスクールの復習のための備忘録です！
いいねお願いします！
### 目的
「deviseのメソッドを使用し、ユーザーのログインの有無を変える」など、アプリ制作において使用頻度の高い分岐を体験し、理解する

##ログイン中のユーザー情報取得
###投稿者本人か確認して表示を変えよう
編集ボタンと削除ボタンの表示部分をif文で囲う
```ruby
~略~
<% if user_signed_in? && current_user.id == tweet.user_id %>
            <li>
              <%= link_to '編集', edit_tweet_path(tweet.id) %>
            </li>
            <li>
              <%= link_to '削除', tweet_path(tweet.id), data: { turbo_method: :delete } %>
            </li>
          <% end %>
~略~
```
`current_user.id == tweet.user_id`と記述することで、
「現在ログインしているユーザー」と「ツイートを投稿したユーザー」が同じか否かを判定することができる

#### &&
`条件A && 条件B`となった場合、条件Aと条件Bが共に真の場合のみtrueが返され、その後の処理が実装される

詳細ページも　if文表示に切り替える
TypeScript - カレンダー日付の妥当性確認(2)

FullCalendarライブラリで日付の移動をするときに、2/31や4/31などカレンダー上存在しない日付であるかチェックをする関数をどうやって実装するか調べました。

前回記事：

https://qiita.com/taitaitai55/items/5c0d8e1446474b77b208

■改善点
Dateオブジェクトを生成するときに「new Date('1/01/41')」とすると、2041/1/1(Tue Jan 01 2041 00:00:00 GMT+0900 (日本標準時))で生成されます。
1年1月41日と解釈すると、カレンダーの日付として存在しないため、厳密なチェックをする必要があります。
厳密なチェックとして、文字数のチェックや正規化を使ったカレンダーの日付の範囲であるかのチェックを関数に加えてみました。

■関数
```Typescript:sample1.ts
// 引数は、年・月・日の３つ
// 戻り値は、true:カレンダー日付として存在、false:カレンダー日付として存在しない
const isValidCalendarDate = (year: string | number, month: string | number, day: string | number): boolean => {
      // 文字列の長さを確認
      let yearLength: number
      let monthLength: number
      let dayLength: number
      if (typeof year == 'number') {
        yearLength = year.toString().length
      } else {
        yearLength = year.length
      }
      if (typeof month == 'number') {
        monthLength = month.toString().length
      } else {
        monthLength = month.length
      }
      if (typeof day == 'number') {
        dayLength = day.toString().length
      } else {
        dayLength = day.length
      }
      // 年:4文字 月:2文字 日:2文字 よりも多い文字数であれば日付として妥当でないとする
      if (yearLength > 4 || monthLength > 2 || dayLength > 2) {
        return false
      }
      //正規表現として形式を確認
      const regex = /^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/
      const yyyyHMmHDd = year + '-' + ('00' + month).slice(-2) + '-' + ('00' + day).slice(-2)
      if (regex.test(yyyyHMmHDd) != true) {
        return false
      }
      // 日付として妥当性があるか確認
      const checkDate = new Date(yyyyHMmHDd)
      if (checkDate && ('00' + (checkDate.getMonth() + 1).toString()).slice(-2) == ('00' + month).slice(-2)){
        return true
      }
      
      return false
}
```

年は4桁であるという正規表現を使ったので3桁の年を引数として渡すとfalseが返ってきます。
百年単位のカレンダーを表示する場合は注意したいと思いました。
C#とVB.NETでExcelからXPSへの変換

## はじめに
データをより良く保存または転送するために、ExcelをXPSに変換するなど、さまざまなフォーマット変換が必要な場合があります。XPSフォーマットは、Microsoftによって開発された電子文書用の標準形式です。PDFと同様に、XPS形式はWindowsオペレーティングシステムで広く使用され、電子文書の作成や共有に使用されます。この記事では、コードでExcelからXPSへの変換方法を紹介します。無料の.NETコンポーネント([Free Spire.XLS for .NET](https://www.e-iceblue.com/Introduce/free-xls-component.html))を使用するだけで、簡単な手順で変換を完了できます。以下に、具体的な方法とサンプルコードを示します。

## ツール
* Visual Studio 2022
* [Free Spire.XLS for .NET](https://www.e-iceblue.com/Introduce/free-xls-component.html)

このライブラリは、無料でExcelファイルを作成、編集、変換することができますが、ページ数の制限があります。または、有料版の製品の[無料トライアル](https://www.e-iceblue.com/TemLicense.html)を申し込むこともできます。
**有料版 :** [Spire.XLS for .NET](https://www.e-iceblue.com/Introduce/excel-for-net-introduce.html)

## インストール方法
1.[Free Spire.XLS for .NET](https://www.e-iceblue.com/Download/download-excel-for-net-free.html)をダウンロードします。
2.Visual Studioで新しいプロジェクトを作成します。
3.「Solution Explorer」>「References」を右クリックし、「Add Reference」>「Browse」を選択します。
4.BINフォルダ内のdllファイルを見つけて、「OK」をクリックします。

## 方法とコード
* **Workbook** インスタンスを作成します。
* **Workbook.LoadFromFile()** メソッドを使用してサンプルドキュメントをロードします。
* **Workbook.SaveToFile(string, FileFormat)** メソッドを使用して、Excel を XPS に変換します。
C#
```c#
using Spire.Xls;

namespace ExceltoXPS
{
    class Program
    {
        static void Main(string[] args)
        {

            //Workbookインスタンスを作成する
            Workbook workbook = new Workbook();

            //Excelサンプルドキュメントをロードする
            workbook.LoadFromFile(@"sample.xlsx", ExcelVersion.Version2010);

            //ExcelをXPSに変換する
            workbook.SaveToFile("result.xps", FileFormat.XPS);
        }
    }
}
```
VB.NET
```vb
Imports Spire.Xls

Namespace ExceltoXPS
    Class Program
        Private Shared Sub Main(ByVal args() As String)

            'Workbookインスタンスを作成する
            Dim workbook As Workbook = New Workbook

            'Excelサンプルドキュメントをロードする
            workbook.LoadFromFile("sample.xlsx", ExcelVersion.Version2010)

            'ExcelをXPSに変換する
            workbook.SaveToFile("result.xps", FileFormat.XPS)
        End Sub
    End Class
End Namespace
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2811193/f9d36d67-8459-3c51-d5ed-fbff0b554447.png)
DBeaverでやらかした話

# DBeaver最強

DBeaverは無料で使えるDBクライアントツールです。
めちゃめちゃ高機能なので、使っている人も多いのではないでしょうか。
**ツールのバグが怖いから、コンソールでのコマンドでしかDBには接続しない** 派の人もいるかと思いますが、私の場合は使いやすいSQLエディタや、見やすい結果セットがあった方が作業ミスが起きにくいと思っているので（むしろコマンドの方がミスる）、DBeaverは開発にも本番メンテナンスでもフル活用しています。

# 本番メンテナンス

ある時、イレギュラーな作業で、本番DBを直接いじることになりました。
大した作業ではなかったので手順書も作らずサクッとやることにしました。

## エディタでクエリを準備

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21645/d00e1063-b31a-1399-23ba-26ca16e725ed.png)

## SELECTで現在のデータを確認と念のため記録

DBeaverの場合、エディタに複数SQLが書いてある場合、全て実行することもできますが、実行したいSQLのどこかにカーソルを置いてcontrol+EnterでそのSQLだけ実行できます

### SELECTだけ実行

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21645/43929cbc-6ac4-2ac4-f905-60afcbdd964f.png)

## 削除を実行

確認が取れたので、いよいよ削除実行です。
**deleteのクエリにカーソルを置いて** 実行っと・・・

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21645/05a86a1c-7f3c-3468-8281-e4a20f38cfb7.png)

## 全消し

データが削除されたか確認しようとしたところ、 **一件だけ消すはずが全て消えてました** 。
DBeaverのもう一つの便利機能で **選択した範囲のSQLを実行する** 機能があります。
そう、カーソルを置いただけのつもりが、1行選択してしまっていて、 **delete from members** を実行してしまっていました。
しかも **Auto commitモード** ・・・。普段はトランザクションモードに切り替えてから実行するのですが、簡単な作業という油断からそれも忘れていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21645/698477bc-a0b3-e3bd-a4f3-baa1a67f4871.png)

バックアップからすぐに復元できましたが、冷や汗をかきました。

# 教訓

- デフォルトをトランザクションモードにしておく
- 更新SQLは実行するまでコメントアウトしておく
    - 選択して `control + /` で簡単にIN/OUTできる
- **実行前にカーソルの状況をよく確認する**

# 願い

更新系SQLは実行するタイミングで **「〜のSQLを実行します。よろしいですか？」** とかSQL確認画面出してくれないかなあ。
cloudflare pagesにNext.js製の静的サイトをデプロイする

## 何の記事？
Next.jsで作った静的サイトをcloudflare pageでデプロイする記事。
公式の説明は以下

https://developers.cloudflare.com/pages/framework-guides/deploy-a-nextjs-site/#use-a-static-export

## 開発環境の構築
まずはNext.jsの開発環境を整えます。Node.jsをインストールし、npxコマンドが実行できるようにします。
やり方は色々あるので、個々人の好きな方法を使ってください。私はwindows使いなので、wingetで入れました。macの人はhomebrewだと思います。
```
npx -v
```
が通れば一旦OK。私の環境では `10.2.4`です。

## 2. プロジェクトの作成
[公式ドキュメント](https://nextjs.org/docs/getting-started/installation)に従って、Next.jsのプロジェクトを作成します。今回は`Next.js: 14.0.4`です。

基本的には以下を実行して、指示に従って選択すればOK。わからなければ全部Yesで良いと思います。
```
npx create-next-app@latest
```

## cloudflare pages用の設定
このままではcloudflare pagesで実行したときに動かないので、`next.config.js`に変更を加えます。

以下のようにstatic exportの設定をします。

```javascript
/** @type {import('next').NextConfig} */
const nextConfig = {
    output: `export`
}

module.exports = nextConfig
```
これでcloudflare pagesにデプロイした際に、buildでコケません(これがないとbuildでコケます)。

## GitHubの設定
cloudflare PagesはGithubと連携させる事で、自動でデプロイさせることが出来ます。
Githubにログインして、新しいrepositoryを作ります。privateで大丈夫です。

作成したら、画面に出てる指示に従って、咲くほど作成したnext.jsプロジェクトをgithubにpushします。

## cloudflare pagesの設定
さて、次はcloudflare pagesでの設定です。
cloudflare pagesに移動すると、以下のような画面が出てくるかと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327331/4d9e716b-6331-0062-92e2-43bed44cccc7.png)

ここで、Connect to Gitを選択するとGitHubと連携出来ます。とりあえず、先ほど作ったレポジトリだけ選べばよいと思います。

その次がこのような画面になるので、先ほど作成したrepositoryを選択します。自分は2つありますが、気にしないでください。

そして選択後に `begin setup`で設定をしていきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327331/0dfc9f9f-11f5-2b84-1946-e9a05eb21af3.png)
以下のような画面が開いたかと思います。ここで具体的な設定をしていきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327331/027cc379-4462-0860-6c63-511c9c4cf033.png)

branchは恐らくmainでしょう。もし違う設定をしていれば変えてください。基本的に、このブランチにpushすると、自動でデプロイが走ります。

Framework presetでNext.js(static HTML Export)を選択します。
すると、下のフォームは自動で埋まります。

で、画面下部の「Save & Deploy」をぽちっとすれば、デプロイが走って、ローンチされます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327331/ae0cae63-55f7-ab0e-8295-71cd999fe115.png)

やったね！
【ChatGPT無】小文字ばかりの英文は区切りがわからない。


　どうも、カーブミラーです。

　今回は、小文字ばかりの英文を大文字小文字の混ざる英文に直してもらおうと思います。

:::note info
本記事は、ChatGPT（無料版）で
行なっております。GPT4での確認
はできておりません。あしからず。
:::

　X-Twitter を見ていると、
　　英文のものも出てきます。

　まぁ、
　　読めないんですけどねw

　それでも
　　最新情報は
　　　欲しいので、
　　　　キーワードを拾ったり、
　　　　　フォローしている方を
　　　　　　チェックしたりして
　　　　　　　得ています。

　結局は、
　　Google翻訳を
　　　使うんですがね(汗)
***
　そんなフォローしている方の中に
　　サム・アルトマン氏がいます。

　この方、
　　普段はスマホでポストしていて
　　　長文は、パソコンを使用して
　　　　いるようです。

　なぜ、わかるのか、
　　というと、大文字小文字の
　　　使い方からです。

　まぁ、スマホアプリで
　　長文を書いている可能性は
　　　ありますけど。
***
　ともかく、
　　サム・アルトマン氏の
　　　ポストのほとんどが
　　　　スマホからです。

　サム・アルトマン氏の
　　解任騒動でも、スマホで
　　　自撮りしてましたし。

　で、ポストが
　　小文字ばかりなので、
　　　区切りがわかりづらい。

　フォロワーさんから
　　文句が出たくらいですw

　そこで今回は、
　　ChatGPTさんで
　　　小文字だけの英文を
　　　　大文字小文字混ざりの
　　　　　英文にして
　　　　　　もらえないかな、と
　　　　　　　思ったわけです。

　では、やり取りをどうぞ。
　　特別なプロンプトは
　　　組んでいませんので、
　　　　あしからず。
***
***User***
以下の文章で、大文字小文字の使い方をチェックして、修正してください。

will keep reading, and we will deliver on as much as we can (and plenty of other stuff we are excited about and not mentioned here)

***ChatGPT***
修正された文章はこちらです：

"Will keep reading, and we will deliver on as much as we can (and plenty of other stuff we are excited about and not mentioned here)."

***User***
変更点は？

***ChatGPT***
大文字化が必要なのは、文の最初の単語だけです。「Will」を大文字に修正しました。

***User***
次の文章もお願いします。

i am slowly making peace with being a public figure, which can be painful. i assume it will get more intense as our systems become much powerful and that's ok.

***ChatGPT***
修正した文章はこちらです：

"I am slowly making peace with being a public figure, which can be painful. I assume it will get more intense as our systems become much more powerful and that's okay."

***User***
変更点は？

***ChatGPT***
修正点は次のとおりです：

- 文の最初の単語 "i" を大文字に修正しました。
- "much powerful" を "much more powerful" に修正しました。
- "that's ok" を "that's okay" に修正しました。

***User***
二番目は、どういう意味ですか？

***ChatGPT***
"Much more powerful"は、より強力なという意味です。文脈に合わせて、より正確な表現に修正しました。

***User***
もとの意味は？

***ChatGPT***
申し訳ありません、修正した文章はオリジナルの意味を変えてしまったようです。元の文の意味を保持しつつ修正するとしたら、次のようになります：

"I am slowly making peace with being a public figure, which can be painful. I assume it will become more intense as our systems become much more powerful, and that's okay."

***User***
変更点は？

***ChatGPT***
修正した箇所は次の通りです：

- "i" を "I" に修正しました。
- "much powerful" を "more powerful" に修正しました。

***User***
下の前とあとの意味を教えてください。

***ChatGPT***
"Much more powerful" は、より強力な、より大きな力を持つという意味です。一方で、"more powerful" は、単により強力であるという意味です。 "Much" は程度や差を強調する際に使われます。例えば、「This laptop is more powerful than the previous model」（このノートパソコンは前のモデルよりも強力です）という文では、「more powerful」はただ前のモデルよりも性能が優れていることを述べています。一方、「This new engine is much more powerful than the old one」（この新しいエンジンは古いものよりもずっと強力です）という文では、「much more powerful」はより強力であることを強調しています。

***User***
OKをOkayに変換する必要は、なぜですか？

***ChatGPT***
実際には、"OK" と "okay" の違いは、基本的にはスタイルや好みの問題です。"OK" はより一般的で短縮された形式であり、"okay" は完全なスペルアウトされた形式です。文章の流れや文脈に合わせて、どちらを使うかを選択することができます。修正する必要性はなく、どちらも同じ意味を持ちます。
***
　こんな感じです。

　なぜか、
　　文章の添削？まで
　　　やろうとしますがw

　プログラムだと、
　　文章の最初の小文字を
　　　大文字に変えるだけで
　　　　終わるんですが、
　　　　　ここが生成AIという
　　　　　　ところでしょうか。

　もう少し、
　　プロンプトを工夫すれば
　　　いい感じになるんでしょうけど
　　　　そこまでの必要性は
　　　　　ありませんw

　ということで、
　　今回は、ここまで。
ElasticsearchでAzure OpenAIを使ったAI Assistantを設定し、生成AIのチャットを利用する

# ElasticのAI Assistant

Elasticではバージョン8.8.1から、AI AssistantというElasticsearchの利用方法をOpenAIなどの生成AIにお尋ねする機能を搭載しています。

https://www.elastic.co/guide/en/security/current/security-assistant.html


普通にAIとチャットするだけの機能ではあるものの、質問に対してElasticsearchとしてのコンテキストを理解して回答してくれるので、大変便利です。この記事もAIに書いてもらおうと思ってちょっと試した結果が以下です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/49a82b4a-f21a-43e8-f4cb-5170dfca3b62.png)

まあなかなか良い結果ではあるのですが、ちょっと概要としてハイレベルすぎる割に急にES|QLやAPMの可視化とか細かい話をし始めたりもしているので、残念ながらそのまま使うのは諦めました。が、十分実用的であることはわかるのではないでしょうか。

この記事では使い方というより、Elasticでの設定方法について（ちょっと一部わかりにくかったので）記載します。

ただし以下の点に注意して下さい。

- この機能はElasticのEnterpriseライセンスが必要です。
- Azure OpenAIのGPT-4を利用します。他は（私は）検証していません。


# Azure OpenAIの設定

ここはElastic関係ないので個人的な備忘録的な側面が強いですが、まずはAzureにAzure Open AIのリソースを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/05944346-6da7-4459-c8ba-1ad16a6b96dd.png)

作成できたら左のメニュー > Model deployments > Manage Deploymentsをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/41fc0360-f03e-9bc2-209e-4cdf43a78bdb.png)

Azure Open AI Studioという画面が開くので、そこのCreate new deploymentから適切なモデルを選んでデプロイします。ここではgpt-4をデプロイしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/677e310e-a335-8e26-0fc2-02469a1fef90.png)

で、この後Elastic側にこのデプロイメントを使うためにエンドポイントURLなどを渡さないといけないのですが、そのURLの取得方法について記載されているドキュメントを見つけられませんでした。色々試してみた結果、Azure OpenAI SturioのPlayground > Chat > View codeをクリックすると表示されるダイアログから、適切なエンドポイントとAPI Keyのどちらも取得できることがわかりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/64f52b12-e573-cba0-b906-34e11eff9a00.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/95e093ef-9adf-8ef8-8d3d-0ea4c6e327a9.png)

ここを参照すると、Endpoint URLは以下の通りでした。

```
https://{OpenAIリソース名}.openai.azure.com/openai/deployments/{デプロイメント名}/chat/completions?api-version={APIバージョン}
```

私はうっかりAPIバージョンをモデルバージョンと取り違えて何度か404エラーになっていました。同様のエラーになった方は注意してください。APIバージョンはこの記事の執筆時点では2023-07-01-previewです。

# Elasticの設定

Elastic側ではKibanaでConnectorとして設定します。Stack Management > ConnectorsからOpenAIを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/19683a58-c3f7-c6b7-bf5d-80ce3d4bcbfb.png)

ここで先ほど取得したエンドポイントのURLとAPIキーを設定します。また、Select an OpenAI providerのプルダウンからAzure OpenAIを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/5e845269-3a39-ac66-faa5-e6bce8644f98.png)

設定したらテストして接続を確認しましょう。

# 使ってみる

AI Connectorが設定できたら、セキュリティやオブザーバビリティーのAI AssistantからAI相手にチャットをすることができるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/e4ea8dab-5c11-2db3-8303-bcc5dfd0a009.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628554/2c2ada47-ebe9-9def-6388-28830acb06e6.png)

例えばセキュリティーのアラートが発生した時に、そのアラートの原因と対処方法などをAIに聞くことで、初動を早くしたりすることができる、というのが特にこの機能がリリースされた際の謳い文句で、確かにその用途が便利かつ有用だと思います。

ただもちろんそういった質問しかできないわけはないので、この投稿の最初でブログ記事を書かせようと試したように、Elasticについて詳しいAIに色々頼み事をしてみることも可能ですので、色々触って新しいユースケースを探してみてください。
Generative Agentsを試してみた


# はじめに
この記事は[Infocom Advent Calendar 2023](https://qiita.com/advent-calendar/2023/infocom)の19日目の記事です。この記事はGenerative Agentsを試してみたの記事になります。

# 目次
1. Generative Agentsとは？
    1. 簡単な仕組み説明
1. 試しにAIエージェントを動かしてみた
1. さいごに
1. 参考文献

# Generative Agentsとは？
ここでのGenerative Agentsとは、前に話題になった論文[Generative Agents: Interactive Simulacra of Human Behavior](https://arxiv.org/abs/2304.03442)のことです。
要は生成系AIを使って、人間の行動をシミュレートできるエージェントを生成しようとした話です。論文では25人のエージェントが互いにコミュニケーションを取り合う姿について話しています。

## 簡単な仕組み説明
AIエージェントアーキテクチャの重要な要素は三つあると書かれています。以下引用は[論文](https://arxiv.org/abs/2304.03442)より引用しています。

![スクリーンショット 2023-12-28 22.00.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2995327/48629fa4-d496-6fc3-bb52-d0395056d56d.png)
[Generative Agents: Interactive Simulacra of Human Behavior](https://arxiv.org/abs/2304.03442)より引用

1. memory stream
> The first is the memory stream, a long-term memory module that records, in natural language, a comprehensive list of the agent’s experiences.

長期記憶モジュールのこと。人間でいう海馬や大脳皮質の辺りかなと思います。

1. reflection
> The second is reflection, which synthesizes memories into higherlevel inferences over time, enabling the agent to draw conclusions about itself and others to better guide its behavior.

内省すること。例えば人と話した際に「この人とは気が合いそうだ」とか「あまり得意なタイプじゃないかも」とか人との関係について振り返ることがあると思います。おそらくそのことかなと思います。

1. planning
> The third is planning, which translates those conclusions and the current environment into high-level action plans and then recursively into detailed behaviors for action and reaction.

計画すること。おそらくイメージはヒューリスティックのことで、自分の価値観や経験に基づいて次の行動や感情に反映させることかなと思います。

この三つの要素がないとエージェントが人間のように振る舞わないらしいです…

# 試しにAIエージェントを動かしてみた
Generative Agentsは[Github](https://github.com/joonspk-research/generative_agents)にも上がっているのでこちらを参考にしました。<br>
* 以下のように少し変更しています
    * 対話だけ行えるように実装、プロンプトを参考にしながら日本語に変更
    * reflectionはユーザーの返事ごとに毎回実行
    * 友好度を設け、ユーザーを「大好き」または「大嫌い」になったら会話を終了
* Agentは以下のようにしました
```Python
@dataclass
class Person:
    name: str = "Mika"
    age: int = 28
    chara: str = "元気、友好的、好奇心旺盛"
    relationship: str = "同い年で仲良し"
    think: str = "最近観た映画の話がしたい"
    memory: List[str] = field(default_factory=list)
```
* 早速会話してみます
    * モデルはGPT-4を使いました
    * 友好度が「大好き」または「大嫌い」を判定しています
        * 基準が分からなかったのでとりあえずお出かけする仲は「大好き」としました
```
私:こんにちは
友好度:7
Mika:  こんにちは、鈴木！最近何か面白い映画を観た？私はこの間、すごく面白い映画を観たんだ。
私:どんな映画？
友好度:7
Mika:  それは「インセプション」っていう映画なんだ。複雑なストーリーだけど、すごく面白くて、ぜひ観てみてほしいな！
私:それは良いね、見に行きたい
友好度:7
Mika:  それなら一緒に観に行かない？今度の週末はどう？
私:いいね！一緒に行こう！
友好度:7
Mika:  それじゃあ、週末の映画の時間と場所を決めよう！何時がいい？そして、どの映画館に行きたい？
私:13時ごろで横浜がよいかな
友好度:10
とても好きなので会話をやめます
```


# さいごに
* Generative Agentsの他にもAgents同士で相談しながら問題解決を目指す[AutoGen](https://github.com/microsoft/autogen)や、人間の欲求も含めたAgentsの[Humanoid Agents](https://arxiv.org/abs/2310.05418)も面白そうだと思っています
* llmを活用し、より人間のように振る舞うAgentsを作れたらと思っています

# 参考文献
* [Generative Agents の論文を読んで、その仕組みをドキュメント問い合わせシステムとして応用してみた](https://qiita.com/kanetugu2018/items/7db3c64196553951715c)
* [【前編】LangChainによるGenerative Agents実装まとめ](https://note.com/mahlab/n/n4760c3d00544)
* [ChatGPTとLangChainで恋愛シミュレーションゲーム用AIを実装してみた](https://qiita.com/z-lai/items/511ebd9d637169cbc5ad)
Webサービス作って公開してみた！

# 概要
- 名前はinfolinkです。infolink.devでアクセスできます。
- 記事投稿サイトです。ジャンルは何でもいいです。(アダルトなどを除く)
- コメントにカテゴリをつけて、間違いを指摘したりできます。
- 指摘があれば通知を受け取れます。期間やカテゴリを設定できます。

# 間違い カテゴリ
修正してわかりやすくしました。というか今までのが伝わりにくかったかもしれません。

間違いは間違いを指摘するときに使います。しかし、一つ問題があります。それは、人によって記事の解釈、文章の解釈が違うことです。人によって解釈が違うので、間違いだと言われても人によっては「そもそも、そんなこと記事に書いてない」と思う人がいます。
なので、9割以上の人が記事に書いてるな、と思うものが間違っている場合のみという条件があります。
要するに、解釈が大きく異なる曖昧な表現には間違いカテゴリは使えないということです(懐疑的も同じく)。

# 懐疑的 カテゴリ
懐疑的は、9割以上の人が記事に書いてるな、と思うものが懐疑的な場合のみです。相応の理由が必要です。
# 懐疑的じゃない カテゴリ
懐疑的に対する反論です。相応の理由も必要。客観的に

# 注意点やルール
カテゴリを使う場合、重複をできるだけ避けてください。見づらかったり、通知が被ったり、反論が分散されたり、とデメリットしかありません。

間違いを指摘する場合、間違いの箇所と事実を正確に伝えるために曖昧な表現を避けたほうがいいです。


# その他
記事やコメントへのコメントを通知として受け取れます。カテゴリと有効期限を指定できます。
エディターはckeditor5です。基本的なマークダウンも使えます。

# おわり
バグや機能リクエストなどは[Twitter](https://twitter.com/infolink_12_22)のDMで気軽にどうぞ。

[infolink.dev](https://infolink.dev/)
[infolinkのアカウント](https://infolink.dev/userprofile/20)
ザ・ダークパターンをshadcn/uiで作成する

# 目的
デザインダークパターンを読んで特に印象に残ったものをshadcn/uiで試してみる

# 前提

## ダークパターンとはなにか

> **ダークパターン**は、主にウェブサイトなどで、ユーザーを騙すために慎重に作られたユーザインタフェースのことである。認知バイアスを利用して、ユーザーが思っているよりも多くの時間やお金を使わせる。または注意を払うように設計されている。

## 開発環境

- Next.js 13
- shadcn/ui

# 印象に残った箇所

## スニーキング（こっそり）

ユーザにとって重要な情報を隠したり、偽装したり、公開を遅らせたりする行為のこと。
該当するケースはショッピングカートにユーザの同意を得ず、勝手に商品を追加する行為です。
ある商品を注文すると、有料オプションが勝手に付属品としてついてきたりするケースもある。こっそりカゴに入れるダークパターンの多くは、有料オプションがデフォルトで選択されていたりもする。
以下はザ・ダークパターンという商品だけをカートに入れたのに、勝手にメッセージカード代がカートに入れられている。
![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3351724/260b2895-de51-ae21-3d40-98a4623c805b.png)


私が実際に体験したものでは、飛行機の比較サイトで決済の最終画面で謎の手数料（画像で言う特別販売利用料）が発生している。
決済画面まで行かないと分からない仕様になっていたため、悪意があると感じてしまった。

![Untitled (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3351724/70b206ac-e21d-a99d-cb2f-4e03bae606ac.png)


## ひっかけ質問

紛らわしい言葉を使ってユーザを特定の選択へ誘導するとパターンです。
例えばチェックボックスに添えられたテキストよく読むと別のことや正反対のことを意味していたりします。
以下は、月額プレミアム会員を辞めるための画面です。いくつかのよろしくない点があります。

![Untitled (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3351724/adc79157-86f6-6142-ec45-f64fd3be09a0.png)

### よろしくない点

- 同じ色が使われているため、どちらが推奨されている動きなのか分からない。
- ユーザーの操作的に直感的じゃない。一般的に左側は否定の動作で、右側は肯定の動作にするのが望ましい。
- ユーザーが推奨するべき動作が左に設定されている。この画面にいる時点でユーザーが推奨する動作は**月額プレミアム会員の資格を失うこと**（=会員をやめること）
- 右側の**キャンセル**は**月額プレミアム会員の資格を失うこと**の**キャンセル**なので二重否定となっており分かりづらい。

実際に作成するとしたらこんな感じになると思っています。

![Untitled (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3351724/0361f516-bb18-e421-1703-e4a54b194f7b.png)

### なぜこのような画面があるか？

ユーザは個人情報を提供したり、望んでもいないことを約束させられていることを嫌うため、ユーザを欺くの表現で強引に同意を得ようとする。

### オプトイン、オプトアウト方式の混在

強引に同意を得ようとする画面の例として、チェックボックスのオプトイン方式とオプトアウト方式が紹介されている。

> **オプトイン（Opt-in）方式**
オプトイン方式では、ユーザーがアクティブに選択肢を選んで同意や購読を行う必要があります。チェックボックスはデフォルトでは未選択の状態で表示され、ユーザーが意図的にチェックを入れることで、メーリングリストへの登録や利用規約への同意などを行います。
例えば、ウェブサイトがニュースレターの購読をオファーする場合、オプトイン方式を用いると、ユーザーは「ニュースレターを購読する」にチェックを入れることで購読を開始します。
> 

> **オプトアウト（Opt-out）方式**
オプトアウト方式では、チェックボックスがデフォルトで選択されており、ユーザーがアクティブに選択を解除することで同意や購読を回避します。つまり、何もしなければ、ユーザーは自動的に購読や同意をしたことになります。
> 

チェックボックスの同意箇所が複数あると、ユーザーは1つ目の文言読み2つ目以降の文言を流し読みする癖がある。このように惰性的にチェックする可能性があるため、オプトイン、オプトアウト方式が混在するようなチェックボックスは使用しない。

以下は1個目のチェックボックスではオプトアウト方式なのに、2個目ではオプトイン方式を使用している。

![Untitled (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3351724/f28033f5-7173-288a-1b1c-9f587ba8cd04.png)


### チェックボックスの使用ガイドライン

- 簡潔な表現にする
- 二重否定などの紛らわしい表現の使用を避ける
- チェックボックスのテキストな肯定文を使用する。
- 1つのチェックボックスで1つの同意得る。(まとめて同意を取らない)
- チェックボックスの方は同意オフは、同意を意味する。(意味を反転させない)

# おわりに

先述した`ひっかけ質問`のように少しでも「？」と感じたUIは改善余地があることに気づけました。
脳のメモリを消費するようなUIを作らないためにも基礎的な部分を固めたいですね…

今回作成したリポジトリはこちらになります。
https://github.com/Suntory-Y-Water/the-dark-pattern-ui-sample
jQueryで実行順序を保証する記述方法

※この記事は僕がぶち当たった問題をとりあえずChatGPTくんに丸投げしてライティングしてもらったものです。

jQueryを使用してWeb開発を行う際、時には特定のコードが正確な順序で実行されることが重要です。
これには、異なる要素や処理が依存関係を持っている場合や、特定の初期化手順が必要な場合があります。
この記事では、そのようなケースで実行順序を確実に保証するためのjQueryの記述方法に焦点を当てます。

# コードの依存関係を理解する
まず初めに、コード内の異なる部分がどのように依存しているかを理解することが重要です。
例えば、特定のDOM要素が存在するかどうかを確認してから特定の処理を実行する場合などが挙げられます。
```js
$(document).ready(function() {
    // 依存関係を確認
    if ($('#myElement').length) {
        // 依存関係が満たされたら処理を実行
        myFunction();
    }
});

function myFunction() {
    // ここで必要な処理を実行
}
```

# Deferredオブジェクトを使用する
jQueryでは、Deferredオブジェクトを使用して非同期処理を扱うことができます。
これを使用すると、異なる処理の完了を待ってから次の処理を実行することができます。
```js
var deferred1 = $.Deferred();
var deferred2 = $.Deferred();

// 非同期処理1
$.ajax({
    url: 'example1',
    success: function() {
        deferred1.resolve();
    }
});

// 非同期処理2
$.ajax({
    url: 'example2',
    success: function() {
        deferred2.resolve();
    }
});

// 両方の非同期処理が完了したら次の処理を実行
$.when(deferred1, deferred2).done(function() {
    // ここで次の処理を実行
});
```

# イベントを利用する
特定の処理を実行するタイミングを制御するために、イベントを活用することもできます。
```js
// イベントが発生したら処理を実行
$(document).on('customEvent', function() {
    // ここで処理を実行
});

// 別の場所でイベントをトリガー
$(document).trigger('customEvent');
```

# まとめ
これらの方法を組み合わせて、特定の実行順序を保証することができます。
それぞれのケースに合わせて最適な方法を選択し、柔軟かつ確実なコードを書くことが重要です。
LOOPだけで円を描画し、円周率を計算する

# 参考にした記事

https://qiita.com/nak435/items/5f55993f1842f8495dbf

https://en.wikipedia.org/wiki/Midpoint_circle_algorithm#Jesko's_Method

出来るだけ理解しやすいロジックに変形してみた

```vb
Option Explicit
Const RR = 200

Sub DrawCircle()
    Dim r  As Long
    Dim x  As Long
    Dim y  As Long
    Dim t  As Long

    r = RR
    y = r

    '中心座標(0,0) 半径rの円を描く
    For x = 0 To r 'Sqr(2) / 2 * r
        '45°ずつ描画
        Call DrawPixel(x, y)
        Call DrawPixel(x, -y)
        Call DrawPixel(-x, y)
        Call DrawPixel(-x, -y)
        Call DrawPixel(y, x)
        Call DrawPixel(y, -x)
        Call DrawPixel(-y, x)
        Call DrawPixel(-y, -x)

        t = t + x
        If t > y Then
            t = t - y
            y = y - 1
        End If
        
        '45°まで
        If x >= y Then
            Exit For
        End If
    Next
End Sub

Sub DrawPixel(ByVal x As Long, ByVal y As Long)
    x = x + RR + 1
    y = y + RR + 1
    ActiveSheet.Cells(y, x).Interior.Color = RGB(255, 0, 0)
End Sub


```


# π　と　√2　を計算してみた

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411106/d286416e-66f3-1a74-d465-f45fbc229fa0.png)


## √2
x=yの時は正方形なので
```2 * x^2 = r^2```
```√2 = r / x```

## π
図形(Rax0) + 図形(yar0) - 正方形(yax0) = 円弧(Rr0)

図形(Rax0)の面積は、Loop中のyの合計 ※積分の考え方
正方形の面積は、```x * y```
円弧(Rr0)の面積は、```(yの合計 * 2 - x * y) ```

円の面積 = 4 * 円弧(Rr0) より
```π = (yの合計 * 2 - x * y) * 4 / r ^ 2 ``` 



```vb
Sub DrawCircle()
    Dim r
    Dim x
    Dim y
    Dim t

    r = 100000000#
    y = r

    Dim sum

    '中心座標(0,0) 半径rの円を描く
    For x = 0 To r 'Sqr(2) / 2 * r
        t = t + x
        If t > y Then
            t = t - y
            y = y - 1
        End If
        
        sum = sum + y
        
        '45°まで
        If x >= y Then
            Exit For
        End If
    Next

    Debug.Print "Sqrt(2)=" & r / x, (r / x) ^ 2
    Debug.Print "π=" & (sum * 2 - x * y) * 4 / r ^ 2
End Sub
```
```
Sqrt(2)=1.41421356474619     2.00000000671213 
π=3.14159274157015
```
Julia v1.10.0 がリリースされました

Julia v1.10.0 がリリースされました(December 25, 2023)。

新しい機能/変更点は色々ありますが，小生が「将来使うかも？」と思ったのは以下のようなもの。

- tanpi is now defined. It computes `tan(πx)` more accurately than `tan(pix)` (#48575).
- `fourthroot(x)` is now defined in Base.Math and can be used to compute the fourth root of x. It can also be accessed using the unicode character ∜, which can be typed by \fourthroot<tab> (#48899).
- `eigvals/eigen(A, cholesky(B))` now computes the generalized eigenvalues (eigen: and eigenvectors) of A and B via Cholesky decomposition for positive definite B. Note: The second argument is the output of cholesky.
- Format specifiers now support dynamic width and precision, e.g. `%*s` and `%*.*g` (#40105).
３分で分かる！動画付き！人工知能の歴史

# 人工知能の歴史

機械学習で学んだことを発信し続けているアウトプット太郎です。
今はE資格取得に向けて取り組み中です。
この記事はこれから人工知能を学びたいや少しでも人工知能に興味のある方や雑学程度にも見て頂ければ幸いです。

また、読むのは苦手だなどの方は３分で簡単に動画にしても説明しているのでよろしければご覧ください。
:point_down:

https://www.youtube.com/watch?v=MaR5_s645io

## 目次
1.第１次AIブーム
2.第２次AIブーム
3.第３次AIブーム

# 第１次AIブーム
キーワード：探索と推論、トイプロブレム
（キーワードは覚えておくといいです。）

1956年ダートマス会議からAIの研究がスタートしました。（AIといっても実は昔から研究が進んでいる）

しかしここでのＡＩの研究というものは、迷路など現実世界の問題よりは、簡単な問題に対して行うことでした。

迷路問題などで、どの経路を辿るとゴールに一番早くたどり着くことができるのかといった問題を解くことを探索といいます。

ここで一番良い経路を考えることを推論といいます。

第１次ＡＩブームではこの探索と推論において主に研究されました。そしてこのような簡単なパズルやゲームのことをトイ・プロブレムといいます。第１次AIブームはこのようなトイ・プロブレムの問題でしか解くことができないため、衰退していきました。
![a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3656436/45e2cbf0-640c-dfd8-4b60-09dbd3ece6e8.png)



# 第2次ＡＩブーム
キーワード：エキスパートシステム

AIに専門家の知識を学習させようと考えました。例えば医者の知識を勉強させることで機械が医者の代わりをするのではないかと考えられました。
しかし難しすぎることいろいろな専門家の知識がかぶってしまいうまく、判断できず衰退してしまいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3656436/8ba7af5b-273d-29b9-c6f3-f25d38948775.png)

# 第3次AIブーム
そして今が第３次AIブームです！
ディープラーニングの普及によって様々なタスクを解くことができました。例えば画像認識において人間と同じかそれ以上の性能で物を識別することができ、自動運転につながるなどや、今では生成系AIの普及も出てきています。
このようにディープラーニングをきっかけに今の第３次AIブームに入りました。

# 参考
機械学習アウトプット：https://www.youtube.com/watch?v=MaR5_s645io

# 最後に
他にも機械学習やAIの分野インスタやXやYoutubeを用いて発信しています。ぜひご覧ください！
C++でデータベースSQLServer2022 クエリーしてみる（ステップ-2.0）

# はじめに
この記事のお題はC++でデータベースSQLServer2022にクエリーしてみる（ステップ-2.0）」です。ステップ-2.0ということはまだうまくいってないことを意味します。

## この記事内容の作業環境
Windows11 Pro 22H2
CPU Intel(R) Core(TM) i3-5005U 2.00 GHz
Microsoft Visual Studio Community 2022 Version 17.4.4
Microsoft Visual C++ 2022
SQL Server 16.0.1000.6 Express Edition
SQL Server Management Studio	19.2.56.2

# お題のデータべース
データベース名 sample
テーブル名 language 
|項目名|項目型|
|:-:|:-:|
|id| int  |
| name  |  nvarchar(50) |

# お題のソースコード
learn.microsoft.comの日本語サイトの下記に公開されているソースコードを使用します。

 https://learn.microsoft.com/ja-jp/sql/connect/odbc/cpp-code-example-app-connect-access-sql-db?view=sql-server-ver16

このサンプルソースは下記のように紹介されています。
>2013 年 10 月から 2019 年 7 月までの間に、このサンプルの C++ ODBC アプリケーションは 47,300 回ダウンロードされました。

# dsn
データソースはこんな感じです。
```mind:mssql2.dsn
 [ODBC]
DRIVER=ODBC Driver 17 for SQL Server
UID=sa
TrustServerCertificate=No
DATABASE=sample
WSID=DESKTOP-078KPRA
Trusted_Connection=No
SERVER=DESKTOP-078KPRA\SQLEXPRESS
Description=test
```


 # 実行結果
 それでは実行してみます。
 コマンドプロンプトが開いた後、下図のようなデータソース選択ダイアログが開きますのであらかじめ用意しておいたDSNファイルを選択します。
![odbc.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3118036/64835c35-2381-08f2-9c4a-d9ec9f42c3fc.jpeg)

パスワードを入力します。言語はjapaneseを選択できます。
![odbc2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3118036/b5d1afec-8102-1125-5a6d-958928de7444.jpeg)

ダイアログを[OK]で閉じますと、コマンドプロンプトに「Connected!」と出力されれば接続しています。その手前の２行が文字化けしていますが、とりあえず無視です。続いてSQLを投入します。
![odbc3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3118036/28d1a1b7-6779-7a71-dad7-b5740a81ce79.jpeg)


無事に結果が返りました。着色されたコンソールをWindowsで初めてみました。

 # おわりに
 すみません、タイムアウトです。実は日本語のDBも用意していたのですが、オリジナルソースのままでは日本語が通らないようでした。ゼロからマイナスに叩き落されたような感じですが、次回ゼロまで挽回できるだろうか。

# 余談
オリジナルソースのままでは下記のヶ所(pwszConnStr = L"";)がポインタに定数を代入しようとして型不一致エラーでビルドエラーとなりましたので、下記のように応急処置しております。

```cpp
if (argc > 1)
    {
        pwszConnStr = *++argv;
    }
    else
    {
        //pwszConnStr = L"";
        pwszConnStr = *++argv;
    }
```
LocalhostをiPhoneから確認する | Windows版

## 背景
Node.jsで起動した3000番ポートをiphoneで確認したい！
と思ったのですが、調べてもMacとiPhone, WindowsとAndroidなどしか見当たらず解決に時間がかかりました。
同じ状況にいる方の手助けになればと思います。


## 結論
ngrokというサービスを使います！


## 開発環境
- Windows 11
- iPhone


## 事前準備
1. ngrokに登録[（こちらから）](https://dashboard.ngrok.com/get-started/setup/windows)
2. 左バーの「Setup & Installation」が表示されていると思うので、AgentsからWindowsを選択
3. `Windowsキー + R` を押してcmdと入力し、コマンドプロンプトを起動 
4. Chocolatoryが入っている場合はコマンドプロンプトから `choco install ngrok` を実行、入っていない場合はウェブサイトに戻ってDownloadから64ビット版をダウンロード
5. ダウンロードした場合は、.exeファイルをCドライブ>Usersに移動
6. コマンドプロンプトで`ngrok config add-authtoken` から始まるコマンドまで実行して通ったら準備完了です


## ポート起動
1. VSCodeのターミナルから起動（今回はnpm startで起動しました）
2. ポート番号を確認し、コマンドプロンプトで `ngrok http ポート番号` （「ポート番号」の部分は数字に書き換えてください）を実行
3. Forwarding欄に出てくるURLをLINEやSlackなどに貼り付け、スマホからアクセス


## 注意点
無料版の場合は、起動するたびにURLが変更されるので注意してください。
VSCode上でのスタイル変更等は都度反映されるので（ホットリロード）起動しなおす必要はないです。便利。

## その他
余談になりますが、VSCodeにはLive Serverという拡張機能があります。
（左バー上の四角いブロックのボタン（カーソルを当てるとExtentionsと出てきます）を押して、検索ボックスにLive Serverと入れると出てきます）

VSCode右下にGo Liveという表示がされていればインストールできています！
もしインストールしても表示されない場合は、`Ctrl + Shift + P` を押してEnterで画面を再起動しましょう。

ルートディレクトリに `index.html` のファイルがあれば、5500番ポートが起動され、index.htmlが表示されます。
この状態でngrokを起動すれば、スマホでも確認可能です！

ホットリロード機能もあり便利なので、ぜひ使ってみてください✨
[オレオレ06] 転章 オートローダー

## 解説
前回のディレクトリ再構成の結果 index.php の require で指定するクラスファイルのパスが長くなってしまいました。長い名前は短いものに比べて憶え辛く脳の負担になります。

また、機能が増えるにつれてファイルを require する機会も増えていくことでしょう。requireは呼び出された時点でファイルを読み込みます。スクリプトの分岐などで結果として読み込む必要のないファイルも読み込むことになります。
このような状況はリソースを消費しパフォーマンスの低下の一因となりかねません。

new したときだけファイルを読み込む。... オートローダーの出番です。

## 目次
1. オートローダークラス作成
1. Request.php修正
1. View.php修正
1. index.php修正

## オートローダークラス作成
下記の記事を参考にしました。
[オートローダはなぜ名前空間がいいのか](https://qiita.com/ukkz/items/5608ee951b367327ecff)
```php:autoLoader.php
<?php

class autoLoader
{
    /**
     * 
     * @var string
     */
    private $project_root;

    /**
     * 
     * @param  string $root
     * @return void
     */
    public function __construct(string $root)
    {
        $this->project_root = $root;
    }

    /**
     * 
     * 
     * @return void
     */
    public function register()
    {
        spl_autoload_register([$this, 'myLoader']);
    }

    /**
     * 
     * 
     * @param  string $class
     * @return bool
     */
    public function myLoader(string $class)
    {
        // 先頭のスラッシュを除く
        $className = ltrim($class, '\\');
        // バックスラッシュを右に倒す
        $className = str_replace('\\', DIRECTORY_SEPARATOR, $className);

        // クラス名からファイルのパスを作る
        $pattern = '|^App' . DIRECTORY_SEPARATOR . '|';
        $replacement = 'app' . DIRECTORY_SEPARATOR;
        if (preg_match('|^Stampede|', $className) === 1) {
            $pattern = '|^Stampede' . DIRECTORY_SEPARATOR . '|';
            $replacement = 'supplier' . DIRECTORY_SEPARATOR . 'stampede' . DIRECTORY_SEPARATOR;
        }
        $className = preg_replace($pattern, $replacement, $className);

        // クラスファイルのパスを作る
        $classFileName = $this->project_root .  $className . '.php';

        // ファイルが読み込める状態でなければfalseを返す
        if (is_readable($classFileName) === false) return false;

        // ファイルを読み込んでtrueを返す
        require_once $classFileName;

        return true;
    }
}
```
## Request.php修正
```php:Require.php
<?php

namespace Stampede\App\Http; // 追加

class Request
{
// 以下略
```

## View.php修正
```php:View.php
<?php

namespace Stampede\App\Http; // 追加

class View
{
// 以下略
```

## index.php修正
```php:index.php
<?php

ini_set('display_errors', "On");

require_once('../autoLoader.php');

use Stampede\App\Http\Request;
use Stampede\App\Http\View;

// プロジェクトのルートパス
$root = '/opt/project/stampede/';

// オートローダー起動
$autoloader = new autoLoader($root);
$autoloader->register();

// リクエストクラスのインスタンス取得
$request = Request::getInstance();

// ビュークラスのインスタンス取得
$view = View::getInstance();

// テンプレート表示
$view->display('hello.html', $request->all());

exit;
```
[オレオレ05] 転章 vendorディレクトリ

## 解説
これまでに作ってきたクラス（Request.php, View.php）はウェブアプリケーションにおける基幹部分であって、たいていのウェブアプリケーションに不可欠な処理を実行します。
このような不可欠な処理はプログラマーの脳負担を減らすためにも透過的であることが望まれます。

透過的にする（脳の意識から遠ざける）工夫のひとつとしてディレクトリを分けて管理します。

今回は supplier/ という名前のディレクトリを切ってそこで管理します。
（vendor/ にしないのは名前の重複を避けるためです）

## 目次
1. ディレクトリ再構成
1. index.php 修正

## ディレクトリ再構成
```:現在までの構成
stampede/
    public/
        index.php
    views/
        hello.html
    Request.php
    View.php
```
```:修正後の構成
stampede/
    public/
        index.php
    views/
        hello.html
    supplier/
        stampede/
            App/
                Http/
                    Request.php
                    View.php
```
```bash
$ cd /opt/project/stampede/
$ mkdir -p supplier/stampede/App/Http
$ mv Request.php supplier/stampede/App/Http/
$ mv View.php supplier/stampede/App/Http/
```

## index.php 修正
```php:index.php
<?php

ini_set('display_errors', "On");

require_once('/opt/project/stampede/supplier/stampede/App/Http/Request.php');
require_once('/opt/project/stampede/supplier/stampede/App/Http/View.php');

$request = Request::getInstance();

$view = View::getInstance();

$view->display('hello.html', $request->all());

exit;
```
---
次の記事
[[オレオレ06] 転章 オートローダー](https://qiita.com/tonoaki_sato/items/cf37ddd53a3fe79161e3)
前の記事
[[オレオレ04] Viewクラスを作る](https://qiita.com/tonoaki_sato/items/f2dd44799a4d638f35e6)
[オレオレ04] Viewクラスを作る

## 目的
* htmlとphpのコードを分離して可読性を高める
* htmlをテンプレート化して可用性を高める
* htmlspecialchars()でタグの埋め込みを無効化する

## 目次
1. Viewクラス作成
1. htmlテンプレート作成
1. index.php でViewクラスを使う

## Viewクラス作成
```bash
$ cd /opt/project/stampede/
$ touch View.php
```
```php:View.php
<?php

class View
{
    /**
     * 
     * 
     */
    private static $instance = null;

    /**
     * 
     * 
     */
    private $templateDir = '/opt/project/stampede/views/';

    /**
     * コンストラクタ
     * 
     * @return void
     */
    private function __construct()
    {
        
    }

    /**
     * インスタンスを取得する
     * 
     */
    public static function getInstance()
    {
        if (is_null(self::$instance) === true) {
            self::$instance = new self;
        }
        return self::$instance;
    }

    /**
     * 
     * @param  string $template
     * @param  array $params
     * @return void
     */
    public function display(string $template, array $params = [])
    {
        // テンプレートファイル取得
        $filename = $this->templateDir . $template;

        // 出力のバッファリングを有効にする
        ob_start();
        // ファイルを読み込む
        require $filename;
        // 出力用バッファの内容を取得する
        $body = ob_get_contents();
        // 出力用バッファをクリア(消去)し、出力のバッファリングをオフにする
        ob_end_clean();

        // 表示
        echo $body;

        return;
    }
}
```
## htmlテンプレート作成
```bash
$ cd /opt/project/stampede/
$ mkdir views
$ touch views/hello.html
```
```html:hello.html
<h3>Hello <?php echo htmlspecialchars($params['name']); ?></h3>

<div>
    <?php foreach ($params['hoge'] as $item): ?>
    <div><?php echo htmlspecialchars($item); ?></div>
    <?php endforeach; ?>
</div>
```
## index.php でViewクラスを使う
```php:index.php
<?php

ini_set('display_errors', "On");

require_once('/opt/project/stampede/Request.php');
require_once('/opt/project/stampede/View.php');

$request = Request::getInstance();

$view = View::getInstance();

$view->display('hello.html', $request->all());

exit;
```
---
次の記事
[[オレオレ05] 転章 vendorディレクトリ](https://qiita.com/tonoaki_sato/items/661389b698b015c9f9b0)
前の記事
[[オレオレ03] リクエストを処理するクラスを作る](https://qiita.com/tonoaki_sato/items/7b9e6348a2fed82342c4)
[オレオレ03] リクエストを処理するクラスを作る

## 目的
* リクエストの処理を分離することでリクエストに起因するエラーを封じ込める

## 目次
1. Requestクラスを作る
2. index.php で GETパラメータを取得する
3. ブラウザで確認する

## Requestクラスを作る
```bash
$ cd /opt/project/stampede
5 touch Request.php
```
```php:Request.php
<?php

class Request
{
    /**
     *
     */
    private $get = [];

    /**
     *
     */
    private $post = [];
    
    /**
     *
     * @var 
     */
    private static $instance = null;

    /**
     * コンストラクタ
     *
     * @return void
     */
    private function __construct()
    {
        $this->get = $_GET;
        $this->post = $_POST;
    }

    /**
     * リクエストクラスのインスタンスを取得する
     * 
     * @return object
     */
    public static function getInstance()
    {
        if (is_null(self::$instance) === true) {
            self::$instance = new self;
        }
        return self::$instance;
    }

    /**
     * 全てのリクエストパラメータを取得する
     * 
     * @return array
     */
    public function all()
    {
        return array_merge($this->get, $this->post);
    }

    /**
     * 任意のGETパラメータを取得
     * 
     * @param  string $key
     * @param  mixed $default
     * @return string
     */
    public function query(string $key, $default = null)
    {
        if (isset($this->get[$key]) === true) {
            return $this->get[$key];
        }
        if (is_null($default) === false) {
            return $default;
        }
        // エラー
    }
}
```

## index.php で GETパラメータを取得する
```php:index.php
<?php

ini_set('display_errors', "On");

require_once('/opt/project/stampede/Request.php');

$request = Request::getInstance();

$params = $request->all();

header('Content-Type: text/plain; charset=UTF8');
echo print_r($params, true);
exit;
```

## ブラウザで確認する
> https://192.168.56.110/stampede/index.php?name=sato&hoge[]=hare1&hoge[]=hare2
---
次の記事
[[オレオレ04] Viewクラスを作る](https://qiita.com/tonoaki_sato/items/f2dd44799a4d638f35e6)
前の記事
[[オレオレ02] シンボリックリンク](https://qiita.com/tonoaki_sato/items/cfdca9b0e0f03f4f0fd6)
[オレオレ02] シンボリックリンク

## 目的
* URLとディレクトリパスを分離することで可用性を高める
* 公開ディレクトリをpublicに絞ることで意図しないファイルの公開を防ぐ

## 目次
* プロジェクトルートを作る
* 公開ディレクトリを作る
* ファイルを移動する
* シンボリックリンクを張る

## プロジェクトルートを作る
```bash
$ mkdir -p /opt/project/stampede
```

## 公開ディレクトリを作る
```bash
$ mkdir /opt/project/stampede/public
```

## ファイルを移動する
```bash
$ sudo mv /var/www/html/stampede/index.php /opt/project/stampede/public/
$ sudo chown [user].[group] /opt/project/stampede/public/index.php
```

## シンボリックリンクを張る
```bash
$ cd /var/www/html
$ sudo rmdir stampede
$ sudo ln -s /opt/project/stampede/public/ stampede
```
---
次の記事
[[オレオレ03] リクエストを処理するクラスを作る](https://qiita.com/tonoaki_sato/items/7b9e6348a2fed82342c4)
前の記事
[[オレオレ01] PHPでHelloWorld](https://qiita.com/tonoaki_sato/items/3f9f3914e8242452115e)
[オレオレ01] PHPでHelloWorld

## 目的
まずは HelloWorld から！

## 目次
1. 前提
1. index.phpファイルを作る
1. コードを書く

## 前提
* ドキュメントルート: /var/www/html
* URL: https://192.168.56.110/

## index.php ファイルを作る
```bash
$ cd /var/www/html
$ sudo mkdir stampede
$ cd stampede
$ sudo touch index.php
```

## コードを書く
```bash
$ sudo vi index.php
```
```php:index.php
<?php
echo 'HelloWorld';
(ZZで保存)

// エスケープキーが効かなくてviのモード切替ができない場合は、
// Ctrl + c でモードを切り替えてから ZZ で保存する

```
---
次の記事
[[オレオレ02] シンボリックリンク](https://qiita.com/tonoaki_sato/items/cfdca9b0e0f03f4f0fd6)
前の記事
[オレオレフレームワークの作り方](https://qiita.com/tonoaki_sato/items/67b52a9bd7fc973e3752)