2024年4月29日月曜日

動的計画法について

 アルゴリズムの定石?なのかもしれませんが、今まで知らずにいました。わかりやすい説明がありました。時間がかかる探索などに有効なようです。これを、実際の場面に適用するのは、また別の難しさがありそうですが。

https://dai1741.github.io/maximum-algo-2012/docs/dynamic-programming/

2024年4月27日土曜日

ウエブ最適化ではじめる機械学習 備忘録 P19 ベイズ更新について

p(y)=∫p(x,y) dx  加法定理         p(x|y)=p(x,y)/p(y) 乗法定理
p(x,y)=p(x|y)p(y) = p(y|x)p(x)  2つ目の=より
p(x|y)=P(y|x)p(x)/p(y)   ベイズの定理
p(Θ|D)=p(D|Θ)p(Θ)/p(D)   p(D)は定数とみなされるため(Θの影響なし)
p(Θ|D) ∝ p(D|Θ)p(Θ) :p(D)証拠(evidence) or 正規化定数(nomalizing constant) p(Θ)事前分布 p(Θ|D)事後分布
  書籍にはないが  p(D)=Σp(D|Θi)p(Θi) も頭に入れておいたほうがいいようだ。 

これ以降は、下記リンクを参照させていただいた。
 https://qiita.com/ground0state/items/210faf0e7f3b0362426b

p(Θ|D1,D2)=p(D1,D2|Θ) p(Θ) / p(D1,D2)
   =p(D1|Θ) p(D2|Θ)p(Θ)/ P(D1)P(D2)
   =( p(D2|Θ)/P(D2) )* ( P(D1|Θ) /P(D1) )* p(Θ)
   = ( p(D2|Θ)/P(D2) )* p(Θ|D1)
p(Θ|D1,D2,D3)=(p(D3|Θ)/P(D3){(p(D2|Θ)/P(D2))* p(Θ|D1)}
       =(p(D3|Θ)/P(D3)) { p(Θ|D1,D2)  }
 再帰的な構造ともいえそう。つまり
  p(Θ|D1,D2,D3...Di)=(p(Di|Θ)/P(Di)* p(Θ|D1,D2,D3...Di-1)
       事後分布=(p(Di|Θ)/P(Di)* 事前分布
 ということなんだろうと思う。
 「事後分布を次の事前分布で使って。。」 という説明があちこちで 見られますが、 最初、なかなかわからなかったけれど、たぶん、こういうことなんだろうと思う。

  p(Di|Θ)/P(Di)を かけていくのですが、上記リンクでは この部分が
   p(x|ω)=ω^x*(1-ω)^(1-x) (ベルヌーイ分布)
  で、x=0で裏なら1-ω x=1で表ならω ωはΘに該当
 
   積分して1になるように定数p(Di)を決めていくということか。

 そして、プログラムの再帰的構造(昔、高校の授業で漸化式でアルゴリズムを教えてもらったことを思い出す)をうまく利用して、コインが表になる確率ωの分布が求まるということのようだ。

2024年4月26日金曜日

実用Git第3版 備忘録5 リモートリポジトリ

 P258 git init -b main fluff  開発(ノンベア)リポジトリの作成

            git init --bare -b main fluff-bare  ベアリポジトリの作成(権威ある参照箇所)

--bareオプションを指定してgit cloneを実行すると、ベアリポジトリが作られる

P261 リモートの作成  git remote           .git/configにリモートがある git configで操作

P263 リポジトリのブランチの分類: リモート追跡ブランチ(リモートリポジトリの追跡が目的)、ローカル追跡ブランチ(開発ブランチとリモート追跡ブランチの変更の両方を集める)、トピックブランチ(開発ブランチ)、リモートブランチ(リモートリポジトリにある)

git branch -a
 main         ローカル追跡ブランチ
 mylocal-branch    トピックランチ(開発ブランチ)
remotes/origin/main     リモート追跡ブランチ

P264  リモートは URLとrefspec(refの対応づけを示す)の2つからなる
      refspecの例: +refs/heads/*:refs/remotes/remote/*   (fetchで利用)

P274 git push origin main   

P277 git branch -a
        *main                                                      ローカル追跡ブランチ
          remotes/origin/HEAD -> orign/main  リモートがアクティブなブランチだと考えているブランチをシンボル名で示す
          remotes/origin/main         リモート追跡ブランチ

P280  git pull は、git fetchのあと、git merge(or rebase)

P284 図で見るリモートリポジトリ開発サイクル
      
   オリジナルリポジトリ    AーB ←main       
                        ↓origin/main ①              
   クローンリポジトリ        AーB ←main ③ ④         

①オリジナルリポジトリのmainブランチはクローンのorigin/mainという新しいリモート追跡ブランチに導入される
②新しいクローンレポジトリのなかで、origin/mainブランチはmain(オリジナルの?)のHEADコミットを参照するように初期化される。ここではBを参照。
③クローン内にmain(ローカル追跡ブランチ)が作られる。
④mainブランチは、オリジナルレポジトリのアクティブブランチのHEADであるorigin/HEADを参照するように初期化される。(Bを参照)
 クローン後、カレントブランチとしてmainブランチを選び、チェックアウト

クローンで、X,Yをコミットすると
   オリジナルリポジトリ       AーB ←main         
                      ↓origin/main   
   クローンリポジトリ        AーBーXーY ←main 

オリジナルで、C,Dをコミットすると
   オリジナルリポジトリ       AーBーCーD ←main         
                      ↓origin/main   
   クローンリポジトリ        AーBーXーY ←main 
 この状態を 履歴が分岐 あるいは フォーク したという

自分の履歴をプッシュしようとするとき、 rejectされる
 (git push -f で強制的に上書きもできるが)
プッシュの前に 自分のリポジトリでマージが必要
 git fetchで、オリジナルを取り込む        
                      CーD ←origin/main  
                      | 
   クローンリポジトリ        AーBーXーY ←main 
あとは mainブランチに origin/mainブランチをマージすると両者を統合できる
   git merge orign/main
コンフリクトが起きたら git reset --hard ORIG_HEADでYにもどることも可

マージ出来たら git push
                        C ー D   
                       |    |
   オリジナルリポジトリ        AーBーXーYーM ←main 

                       C ー D   
                      |    |
   クローンリポジトリ        AーBーXーYーM ←main 
                           ↑origin/main


2024年4月25日木曜日

実用Git第3版 備忘録4 コミットの書き換え 一時退避

P199 履歴の書き換えについて 注意すべきこと:共有リポジトリに組み込まれたブランチの一部を改変、改ざん、改良してはならない。

P201 A-B-C-D-E-F-G  のコミットDに問題があり、取り消したい場合
    git revert main~3
          A-B-C-D-E-F-G-D'   D'はコミットDの逆(Dの効果を打ち消すようなコミット)

P202 git commit --amend コミット後ログメッセージを修正できる。
 それ以外の活用方法として:
  例:①誤ったコミット ②ファイルを再編集して、新たにコミット もできるが、
 コミット履歴をきれいに残すため、①直接書き換え、必要に応じてファイルを追加、削除できる。②git addやgit rmでインデックスに変更を反映、③git commit --amendを実行
  ①speech.txt修正  git diffでインデックスと作業ディレクトリの差分チェック
  ②git add speech.txt   ③git commit --amend 必要ならコミットメッセージ編集
   git show-branch --more=5      git shouで確認
      A-B-C HEAD  を  A-B-C' HEAD  に変更したというイメージ

                                             ↓dev HEAD                                    ↓dev HEAD
P205 git reset           A-B-C-D                                                 A-B-C-D  
      オプションの影響       HEAD    インデックス     作業ディレクトリ
         --soft       〇
         --mixed        〇        〇
         --hard        〇        〇           〇

--softの例:①file1コミット②file2をステージング③file1のコミットとfile2のステージングした変更をひとつにまとめたい④git reset --soft HEAD^ ⑤git commit -m 'file1 And file2'
  ※インデックスには影響与えてないので、ステージングはそのまま使えて、コミットだけでいいということ。

--mixedの例:①file3とfile4作成した ②コンテンツにもとづき、コミットの順序をきめたい③ git reset HEAD^ (--mixedはディフォルトなのでオプション指定なしでもOk) ④git add file4    git commit -m 'content from file4'  ⑤git add file3    git commit -m 'content from file3'  ⑥git add file1    git commit -m 'content from file1'  などのように
 ※作業ディレクトリには影響あたえないが、ステージングはリセットされるので、再度、ステージングを順序を変えて行うことができるということ。

--hardの場合は、作業ディレクトリもインデックスも含めリセットされるので、ファイルの作り直し、ステージングのやり直しをした上で、コミットすることになる。

  

P214 git cherry-pick  指定したコミットFをカレントブランチに加える 以下の場合はF’

   A-B-C-D-E-F-G-H  dev          A-B-C-D-E-F-G-H  dev

       |                                        |

       V-W-X-Y-Z   rel_2.3             V-W-X-Y-Z-F'   rel_2.3

     git checkout rel_2.3     のあと    git cherry-pick dev~2

P217  reset,revertとcheckoutの関係

  ブランチを移るとき:git checkout またはgit switch

      git resetはブランチ切り替えはしない:ブランチ名指定するとカレントブランチをリセットしてしまう git rest --hardは既知の状態を復元することが目的

P218 他の開発者があなたの李ぴ時取りをクローンしたり、一部のコミットをフェッチしたりしているときは、リポジトリ内のコミット履歴を書き換えるようなコマンドを使うべきではない。代わりにgit revertを使う。git resetやgit commit --amendを使ってはならない。

P219 rebaseについて:書籍にはないが、簡単な例で試してみる

①topicブランチで git rebase main ②mainブランチで git merge --no-ff topic

(--no-ffはfast-forwardなしで、そのほうがあとで管理しやすいとのこと)

③git log --graph で確認できる

もし、①で競合が発生したら、②エディタで競合の原因(main_file)を編集し、競合を解決 ③git add main_file  git rebase --continue(P220)  ④mainブランチで git merge --no-ff topic

P221 rebaseを途中でやめたいとなったら git rebase --abort

P225  git rebase -i コミット  とすると、指定したコミット以降のコミットが編集可

エディタで先頭のpickをsquashにすると、直前のコミットの結合され、新しいコミットメッセージのテンプレートができる。先頭の#は無視される。


P237 git stash list 一時退避されたコンテキストの表示
   git stash -m コメント  でインデックスと作業ディレクトリの内容全体を一時退避          git stash popでもどす
git stash apply + git stash drop =git stash pop

例:  ①file1とfile2を作成、add、commit ②file1に追加文字 ③git stash -m 'Tinkerd file1'
④git commit --dry-run 仮コミットしてみると、コミットするものなしと表示、git stashでまったく、残っていない状態であることを示している? 
⑤ file3を作成 ⑥git stashしても失敗 新しい未追跡ファイルは-uが必要  ⑦ git stash -u -m 'add new untracked file3'  
# 変更を避けておく
$ git stash


利用例1: ブランチを切り替えて、他の作業をする>元の作業ブランチに戻る>スタッシュを今いるブランチに適用  git stash apply
  ブランチを切り替えるためだけに中途半端な状態をコミットしなくてよい

利用例2:コミット…と思ったらブランチ間違いに気づいた>コミットしないままブランチの切り替えに成功すれば良いが、コンフリクトする場合など切り替えられないこともある
git checkout  proper-branchでエラー>git stash> git checkout proper-branch>スタッシュを今いるブランチに適用 git stash apply

利用例3:コミットしてしまったとき>直前のコミットを取り消して、コミット前の状態に戻す。インデックスは残すので--softで  git reset --soft HEAD^ > git stash> git checkout proper-branch>スタッシュを今いるブランチに適用する git stash apply

P246 参照ログで、git reset --hardで削除してしまったものを復活
ログで見えなくても、 git reflog で表示できる。それをもとに、もどりたいところを指定して  git reset --hard HEAD@{1} > git log で確認

2024年4月24日水曜日

実用Git第3版 備忘録3 マージ 差分 bisect

 P134 作業ディレクトリのファイルを書き換えたり、git addやgit rmでインデックスを書き換えると、リポジトリの作業ディレクトリやインデックスはダーティ状態になる。ダーティ状態でマージは簡単にはいななくなる。クリーンな状態にしてからマージが原則。

P169  git diff    作業ディレクトリとインデックスの差分
        コミットする準備ができているかチェックできる
          git diff --cached commit  インデックスと指定されたcommitとの差分
          git diff commit   作業ディレクトリと指定されたcommitとの差分
             git diff HEADなど
          git diff commit1 commit2 

P189  git bisect start        コミットが変わるたび  git bisect goodかbadを繰り返す。
     どのコミットから正常動作したか等チェックできる


実用Git第3版 備忘録2 ステージング

 P118 git commit --all   ステージングしてないものもすべてコミットできるが、新規のサブディレクトリとその中のファイルはコミットされない
P122 git rm data  コミットしたファイルを削除(インデックスと作業ディレクトリの両方から削除)
   復活したいなら  git checkout HEAD -- data   (--はファイル名であること明示)

P120 git commit -a -m trackedfile リポジトリすでにあり、追跡されているファイルを書き換えたときには、git addとgit commitの2つのステップを結合できる。しかし、ファイルを移動、削除した場合には、そうはならない。2つのステップを別々に実行する必要がある。 git rm somefile         git commit

P129  ほぼすべての.oは無視するが、vendor_filesサブディレクトリのdriver.oは追跡したい場合は、  上の階層のディレクトリの.gitignoreに*.oとして、vendor_filesサブディレクトリの.gitignoreに!driver.oというようにするとよい。

P130  git check-ignore my_package/anotherfile で、.gitignoreにひかっかるかどうか確認できる。
 git check-ignore -v my_page/anothrefileで、
    .gitignore:3:my_package/   my_package/anotherfile
   というように、.gitignoreの3行目のmy_package/の設定に適用されているということがわかる。
      


実用Git 第3版 備忘録1 ブランチ

 P66  新ブランチを作成するためのコマンドの基本的形式は次のとおりである。

 git branch branch-name  start-point

  start-pointを指定しなければ、ディフォルトで現在のアクティブブランチの先頭(HEAD)コミットが使われる。

P67 git branch ローカルのみ  git branch -r リモートのみ  git branch -a ローカル、リモート両方

P73 コミットしていない変更があるときのチェックアウト

  Please ,commit your changes or stash them before you can switch branches.

    ブランチを切り替えるためには、先に変更をコミットするか、一時退避(stash)

  stashしたあと、 git stash applyを切り替え先で行うとマージが必要

P74 ファイルの状態を復元

     git checkout dev~4 index.js  特定のファイルを4世代前のものに戻す

  rm -rf server.js      git checkout server.js  削除されたファイルを復元

      git restore [options] file という新コマンドも使える

P75 別のブランチへの変更マージ

  git checkout -m dev  

  作業ディレクトリの現在の状態と切り替えようとしているブランチの状態に矛盾がある場合、作業ディレクトリで加えた変更を活かした形で新ブランチに切り替えたい場合に、ターゲットブランチへの切り替えと同時にマージを実行。3方向(下図のA,A1,A2の3方向という意味か?)マージであることに注意。

   A2 (dev)

   ↓ 

          A←A1(main:コミットしてない)

P77 マージされたブランチのベースブランチ情報:枝分かれの起点をさがすコマンド

   git merge-base original-branch  new-branch  で起点のコミットIDが表示

   ブランチの起点となった最初のコミットは明示的に示されていないので、そのコミット(起点)は、新しいブランチ(new-branch)が作成されたもとのブランチ名(original-branch)からアルゴリズムによって見つけられるようになっている。

      new-branch    

  ↓ 

      起点←original-branch       

P96 git log --graph  コミットグラフが枝分かれも入っていてみやすい

P101 gitkというツールをインストールしてみた。コミットグラフが表示されてわかりやすい。

2024年4月23日火曜日

detached HEAD 、Reject などよくあるミス対策をまとめてみた 備忘録

 https://kaityo256.github.io/github/advanced/index.html

を参考にさせていただいた。(以下抜粋)

1)detached HEADの対処

git switchとgit restoreは追加された機能。以前はgit checkoutが使われていたが、git checkoutに役目が多すぎたためにコマンドが分けられたとのこと。

$ git log --oneline
9b662ef (HEAD -> main) test

$ git checkout 9b662ef  すると、以下の説明が出てくる
Note: switching to '9b662ef'.You are in 'detached HEAD' state.  
  git switch -c <new-branch-name>
Or undo this operation with:  git switch -

ブランチを介さないでGitを操作するのは事故のもと。
git switchは直接コミットを指定することはできず、コミットハッシュとブランチ名を同時に指定する必要があるので、安全。

$ git switch -c newbranch 9b662ef というように。

git checkoutの代わりにgit switchを使った方が良い。
(同様な理由でファイルの修正を元に戻すのもgit restoreを使った方が良い。)

※detached HEAD になったら「ブランチをつけてmainに戻る」が原則。

2) Rejectの対処
 git push で ! [rejected]      すでに、originが変更されているのに気づかず、pushするとrejectされてしまう。
 git fetchで ローカルのorigin/mainがリモートの作業を反映したコミットを指す。ローカルのmainと、origin/mainは、同じコミットから歴史が分岐した状態になる。
 あとはマージする。
 git merge origin/main  衝突したら、適切に修正してgit add、git commit
 これで、リモートのmainと歴史を共有しているので、そのままgit pushができる。

 

2024年4月19日金曜日

クラウドについて

「クラウド人材」とはクラウド技術やクラウドサービスを設計・実装・運用する提供側の人材を意味する。AWSや Azureを活用したり、認定資格を取得したりすることではない。」とIPAの登氏が述べている。

https://japan.zdnet.com/article/35217768/ 

 日本は、クラウド人材を育成しようとしたけど、結果的に、クラウド人材できず、高額な使用料を外資系クラウドに払い続けることになってしまった。障害が発生してもブラックボックスなので解決不可能とのこと。今後は、国内でも安定したクラウド環境ができていくことを期待したいものです。

reactを使ってみた

sudo npx create-react-app react-sample --template typescript

では、だめで、sudoなしだと、うまくいくようだった。

node関連のインストールを行うが、バージョンが合わないといろいろ設定に手間がかかるようだった。

なお、警告が出たので

npm WARN deprecated tar@2.2.2: This version of tar is no longer supported, and will not receive security updates. Please upgrade asap.

npm install tar@6 -gtar としたら、解決


Next.jsのほうは、細かい変更もあるらしく書籍の情報だと動かなかったりする。

のように、pagesフォルダがappフォルダに変わってルール違っていたりする。
現在進行形の技術なので、ネットの最新情報も大事なようだ。
  pages/sample.tsx  >>>> app/sample/page.tsx  で/sampleのURLに対応
それから、getStaticProps 相応のものは不要らしい。コンポーネントの中に直接書いてもOkになったらしい。

 また、動的ページの場合 localhost:3000/posts/**ここの部分**
ならばapp/posts/[id]/page.tsxの中身は
  export default function Test({ params }: { params: { id: string } }) {
  return (
    <div>
        <p>Test: {params.id}</p>
    </div>
  );
}としてやれば、**ここの部分**が、idに取り込まれ表示される。
せっかく、React本買ったけど、古くて試すのがけっこう大変。が、この修正が
けっこう勉強になったりするのかもしれないが。。

  

2024年4月18日木曜日

車のエンジンが安定しない

 18年目になる車なので、あちこち不具合も出やすい。一度、プラグがオイル汚れが原因で調子悪かった時があり、修理してもらったが、また似たような症状出ている。イグニッションコイルもまだ大丈夫なはずだし。

 試しに、プラグへのケーブルの差込口すべてに、接点復活材をつけてみたら、少し安定してきたような気もする。イグニッションコイルへのヒューズも念のため、接点復活材をかけておいた。昔、カローラに乗っていたとき、エンジンが安定しないときがあり、ディーラーでもしばらく原因をつかめなかったが、しばらくしてプラグコードの断線が原因だったことが判明。エンジンの不安定は、電気系統が原因になっていることが多い感じもするので、これで、少し様子をみてみたい。

2024年4月16日火曜日

ChatGPTのAPI用コードをC#で

 ネット上には、C#の例がなかったので、curlのサンプルをもとに、ChatGPTも活用しながら、コードをつくったら、うまくいった。

        string apiKey = textBox4.Text;
        string apiUrl = "https://api.openai.com/v1/chat/completions";
        var requestData = new
        {
            model = "gpt-3.5-turbo",
            messages = new[]
            {
       new { role = "system", content = "ウオーキング愛好者です" },
       new { role = "user", content = "東京都内のおすすめな散歩コースは?" }
    }
        };
        string requestDataJson =     Newtonsoft.Json.JsonConvert.SerializeObject(requestData);
        using (var httpClient = new HttpClient())
        {
            var request = new HttpRequestMessage(HttpMethod.Post, apiUrl);
            request.Headers.Add("Authorization", $"Bearer {apiKey}");
            request.Content = new StringContent(requestDataJson, Encoding.UTF8, "application/json");
            var response = await httpClient.SendAsync(request);
            string responseBody = await response.Content.ReadAsStringAsync();
            MessageBox.Show(responseBody);
        }

 あとは、responseBodyのパースを行う方法を実装する予定

2024年4月11日木曜日

vs-code live serverがwslで動作しない

 wslでLive Server使おうとしたらブラウザが開かない現象があった。

settignsを以下のようにしたら解消した。

{
    "workbench.colorTheme": "Default Light+",
    "liveServer.settings.donotShowInfoMsg": true,  
    "liveServer.settings.useLocalIp": true,
    "liveServer.settings.CustomBrowser": "chrome",  
    "liveServer.settings.AdvanceCustomBrowserCmdLine": "/mnt/c/Program Files/Google/Chrome/Application/chrome.exe"
}

chrome.exeの場所はWin11の場合です。他のOSだと、調べる必要あるようです。

2024年4月8日月曜日

raspiでdocker(備忘録)

 raspiでもdockerが使えそうなので試してみた。

https://tech-lab.sios.jp/archives/27798

上記が参考になった。ただ、OSは64bitでないとだめだった。dhcpcd.confの設定で手間取った。固定にするための設定が、こちら

interface eth0
static ip_address=192.168.1.ここに数字/24
static routers=192.168.1.1
static domain_name_servers=192.168.1.1 8.8.8.8
profile static_eth0
fallback static_eth0
順不同

sudo docker run -d --name=nginx -p 8080:80 nginx
が正しいコマンド

docker-composeは32bitではうまくいかなかった。それもあって、64bitに入れなおしたのですが。。docker-composeは非常に便利。
sudo docker-compose up と、管理者権限が必要なようだ
この方法で、WordPressなどもインストールができた。
(ただし、64bitならymlではarm64v8/wordpressと指定してやらないとだめだった。)

よく使うコマンドは

docker ps -a (コンテナID表示)
docker stop コンテナID
docker rm コンテナID

docker rmi イメージID

docker image ls
docker login
docker image push [イメージ名] でpush
docker image pull nginx

複数コンポーネントを扱う時は
docker-compose exec wordpress bash

コンテナ内の確認
docker exec -it コンテナ名かID bash   抜けるにはexit
エディタがないのでインストール必要
docker restart コンテナ名 で反映

バックアップしたいときは
docker cp <container-name>:/var/www/html/content /path/to/backup/directory
docker exec <mariadb-container-name> /usr/bin/mysqldump -u <username> -p<password> <database_name> > /path/to/backup/directory/backup.sql
等など 


2024年4月7日日曜日

minikube セット

 https://qiita.com/trtrbohz/items/f7357d44b2b3d08d9c24

リンクを参考に、Wslでセットしてみたが、権限の関係でうまくいかない

minikube start --driver=docker --force として、--forceをつけたらうまくいった。

https://zenn.dev/yukiko_bass/articles/397e3270f1f3de

にあるように、minikube dashboardで、ローカルのリンクをクリックすると

ブラウザで状態を確認することもできるよう。まだまだ奥が深いようで、概要を把握するだけでも、時間はかかりそうです。

※その後、しばらくして 試したら

 Exiting due to HOST_JUJU_LOCK_PERMISSION: というエラー

rm /tmp/juju-*で解決した。rootユーザーと非rootユーザーの切り替えあたりが関係しているとか、ネットにはでている。

コマンドだけで、簡単にサーバが追加できたり、なかなか便利なものだと感心する。そのためのコマンドを理解してないとだめなわけですが。

2024年4月6日土曜日

Vmware、raspiなどでWordPress ディフォルトの設定に注意が必要

 最新版で試したが、ディフォルトでは、投稿などが保存されなかった。設定のパーマリンクのところは、[基本]にするとうまく動作するようになった。何らかの環境の違いが影響している?と思われる。


Haskell入門 関数型プログラミングの基礎と実践 P.212

最近は、久しぶりにHaskellの本を読んでいる。 以前学んだことはだいぶ忘れている。
半分まで読んだあたりで、複数ファイルをConcatして表示するというコードがあったので、試したら出力はstdoutのみだった。試しに、ファイルにも出力できないかと思い、書き換えてみたが、うまくいかない。ChatGPTの助けを借りて、なんとかできた。

module Main where
import Control.Exception (bracket, finally)
import Control.Monad (forM_)
import System.IO
 ( hFlush,stdout,Handle,FilePath,IOMode(..)
   ,openFile,hClose,hIsEOF,hGetLine,hPutStrLn
 )
import System.Environment (getArgs)
import Data.Char (toUpper)
main :: IO()
main = do
     filePaths <- getArgs
     hdl2 <- openFile "output.txt" WriteMode
     concatMultiFiles filePaths hdl2
concatMultiFiles :: [FilePath] -> Handle -> IO()
concatMultiFiles filePaths dst =
  forM_ filePaths $ \filePath ->
    bracket
      (openFile filePath ReadMode)
      hClose
      (\hdl -> copyFile hdl dst)

copyFile :: Handle -> Handle -> IO()
copyFile src dst = loop
 where
  loop = do
    isEof <- hIsEOF src
    if isEof
      then return ()
      else do
        line <- hGetLine src
        hPutStrLn dst line
        hPutStrLn stdout line
        hFlush dst
        loop

ポイントは、最後から2行目のhFlush dstを追加したことだった。