ログ日記

作業ログと日記とメモ

関数型プログラミングの考え方を理解するための昔の記事

この前
関数型プログラミングはまずは純粋関数型言語を用いて、考え方から理解しよう
関数型プログラミングはまず考え方から理解しよう - Qiita
ここを読んだ。

関数型プログラミングの考えを学ぶには、純粋関数型言語で学ぶべきである

これは同意なんだけど、サンプルコードがしっくり来ない。


こんなに複雑になる?というかオブジェクト指向のコードを関数型っぽく変換ということに無理があるような。
仕様を満たすコードをゼロから考えたら、全然変わってくるような。

ということでちょっとHaskellで書いてみた。

data Dish = Karaage deriving Eq
data Bento = Bento Dish Int deriving Eq

単純な数値のリストかタプルでもいいんだけど、オブジェクト指向に寄せてデータ構造を書く。

eat :: Bento -> Bento
eat (Bento s a) = Bento s (a - 1)

replaceList :: Bento -> [Bento] -> [Bento]
replaceList m a = replace a
  where
    replace [] = []
    replace (x:xs)
      | m == x = eat x:xs
      | otherwise = x:replace xs

「データを1減らす」という処理と「リストを一つ入れ替える」という処理を書く。
「リストから最大のものを1件探す」という処理は maximumが使えるので、そのまま使う。

それからユーティリティ的な関数、「xに関数fをn回適用した結果を返す」を作る。

nest :: (a -> a) -> a -> Int -> a
nest f x n = (iterate f x) !! n


最後に

bentoList :: [Bento]
bentoList = [Bento Karaage 10, Bento Karaage 8, Bento Karaage 6]

main :: IO ()
main = putStrLn $ show $ nest (maximum >>= replaceList) bentoList 5

一気に関数を接続して、計算。
※ n回繰り返したときの処理ログが欲しいわけではなく、最後の結果が欲しいということだと読み取ったので、途中経過は出力しない。


関数型プログラミングっていうかHaskell独自のことかもしれないけど、とりあえず細かい関数のパーツを作っておいて、繋ぎ合わせて大きな機能を作るのが関数型プログラミングの考え方っぽいと思える。



昔見たブログのやり取りはとても良かった。
Haskell でグローバル変数が欲しい理由 - あどけない話
グローバル変数が欲しい理由?http://web.archive.org/web/20100702191552/http://www.sampou.org/cgi-bin/haskell.cgi?%A5%B0%A5%ED%A1%BC%A5%D0%A5%EB%CA%D1%BF%F4%A4%AC%CD%DF%A4%B7%A4%A4%CD%FD%CD%B3%A1%A9
「グローバル変数が欲しい理由?」の考察 - あどけない話
URLが変わっちゃったので再度メモ。





以下全体のコード。

module Main where

data Dish = Karaage deriving Eq
data Bento = Bento Dish Int deriving Eq

instance Show Dish where
  show Karaage = "唐揚げ"
instance Show Bento where
  show (Bento a b) = show a ++ show b ++ "個"
instance Ord Bento where
  compare (Bento Karaage a) (Bento Karaage a') = compare a a'

nest :: (a -> a) -> a -> Int -> a
nest f x n = (iterate f x) !! n

eat :: Bento -> Bento
eat (Bento s a) = Bento s (a - 1)

replaceList :: Bento -> [Bento] -> [Bento]
replaceList m a = replace a
  where
    replace [] = []
    replace (x:xs)
      | m == x = eat x:xs
      | otherwise = x:replace xs

bentoList :: [Bento]
bentoList = [Bento Karaage 10, Bento Karaage 8, Bento Karaage 6]

main :: IO ()
main = putStrLn $ show $ nest (maximum >>= replaceList) bentoList 5

haskell-language-server の設定の途中

https://n314.hatenablog.com/entry/2021/07/20/203555 の続き。



https://haskell.e-bigmoon.com/hie/emacs.html
https://haskell.e-bigmoon.com/posts/2020/07-12-haskell-language-server.html

git clone https://github.com/haskell/haskell-language-server.git
cd haskell-language-server
# stack ./install.hs hls
stack ./install.hs hls-8.10.4

GHCバージョンが新しくなりすぎないように、バージョンを指定する。stack ./install.hs help で見れる。
8.10.4 は resolver: lts-17.12 ?


emacsのパッケージでエラーが出るので
https://github.com/emacs-lsp/lsp-java/issues/142
ここに書いている通り

("gnu" . "http://elpa.gnu.org/packages/")

を加える。
melpa の stable は除外した。

package-install でlsp-mode、lsp-ui、lsp-haskellをインストール。エラーが出るので dash も入れる(更新?)。


C-c C-l でstackを使う設定。
https://qiita.com/t-mochizuki/items/d831df3a920108e2d83c


~/.stack/config.yaml に allow-newer: true を付けて stack install cabal-install する。




・・・

最新版で入れ直したり色々やったけど、LSP[Disconnected] になってる。
うーん分からん…。
とりあえず、エラーは出るしC-c C-l も出来るようになったんだけど、型注釈の補完ができない。あとエラーをいい感じに表示する方法が分からない。
これはlsp-haskellで調べずに、lsp-mode とかで調べないといけないのかな。

Googleアナリティクス4プロパティで使われているフロントエンドのツール

メインのプログラムは<script id=base-js ・・・となっている場所にある。
あとはHTMLやJSのcopyrightなどを見ていく。
依存関係は知らないので(angularを使えばlessとmaterialは勝手に付いてくるよ!など)見つけた順に書いていく。

今までと大きな違いは無い感じ。
Angular用の細かいライブラリは省略。

StackでHaskellの環境を作れなかったログ

今のPCにGHCが無かったので入れた。
どうやって環境を作ったのか忘れていて、どこかに書いた気がするけど見つからないのでメモ。
うーん…stackの一通りの使い方とか、前にも書いた気がするんだが…。GHCJSのことを書いて肝心のGHCのことは書いてなかった?

sudo apt-get install g++ gcc libc6-dev libffi-dev libgmp-dev make xz-utils zlib1g-dev git gnupg netbase
https://docs.haskellstack.org/en/stable/install_and_upgrade/#linux

今の環境だと全てインストールされていたので実行する必要はなかった。無い場合は入れる。
https://get.haskellstack.org/stable/linux-x86_64.tar.gz
バイナリを取ってきて ~/bin/ 等にコピー。


最初のhello worldを実行。

stack new helloworld new-template
cd helloworld
stack build

まだ環境が何もないのでダウンロードが始まる。 ~/.stack/ に環境が作られる。


ghc-modをインストール。

stack install ghc-mod

エラーでインストールできない。


Failing to build ghc-mod with stack · Issue #940 · DanielG/ghc-mod · GitHub

~/.stack/global-project/stack.yaml でLTSのバージョンを変える。

resolver: lts-9.21

ちょっとバージョンが古くなってしまったけれど、これでいけた。

stack install intero

次はintero-modeが動かない。
intero-modeのページにも、別のツールを使えと書いていた。

ghc-modのページにも、古いから haskell-ide-engine を見ろと書いていて、haskell-ide-engine のページには haskell-language-server を見ろと書いていた。カオス。あんまり複雑なのは嫌なんだけど…。


https://n314.hatenablog.com/entry/2019/06/01/191056
ここで色々やったみたい。うーん諦めて新しい方のツールを入れるべきか。

TCPDFで円とベジェ曲線で図形を描く

TCPDFでちょっとした図形を入れたい場合、画像を使わずに直接PDFに出力してしまう方法がある。
TCPDFのソースとマニュアルを見ていたらベジエ曲線が簡単に描けるようなのでやってみた。
ベジェ曲線?どっち?昔聞いたときはベジエ曲線と発音されていたような気がする。)


まずは円弧を描く。

<?php
// (snip)
    $x = $pdf->GetX();
    $y = $pdf->GetY();
    $w = 20;
    $h = 20;

    $cx = $x + $w / 2;
    $cy = $y + $h / 2;
    $cw = $w / 2;

    $start = 0;
    $end = $start + 255;

    $pdf->Circle($cx, $cy, $cw, $start, $end, 'D');

角度は右が0度で半時計回りに指定するようだ。
これで右から255度分の円弧を描ける。

ベジエ曲線は、開始座標+3座標を指定する。

<?php
// (snip)
    $ax = $cx + $cw * cos(deg2rad($end));
    $ay = $cy - $cw * sin(deg2rad($end));
    $aw = $x + $w - $ax;

    $ax1 = $ax + $aw / 2 * $sign;
    $ax2 = $ax + $aw * $sign;
    $ax3 = $ax + $aw * $sign;
    $ay1 = $ay;
    $ay2 = $ay + $w / 3;
    $ay3 = $ay + $w / 2.5;
    $p1 = [$ax1, $ay1, $ax2, $ay2, $ax3, $ay3];

    $pdf->Polycurve($ax, $ay, [$p1], 'D');

円弧の端の座標をsinとcosで求める。
角度は$endをラジアンに変換する。deg2radっていう組み込み関数があったんだね。

そこからなめらかになるように、座標を配列で指定して Polycurve 関数で出力する。[$p1]となっているところは、複数繋げると複雑な曲線が描ける。

tcpdf-bezier

こんな感じに線を引けた。

中を塗る場合は、反転したベジエ曲線切らずに繋げて空白を埋めるように工夫する必要がある。

<?php

require_once 'vendor/autoload.php';


function drawCircle($pdf, $reverse)
{
    $x = $pdf->GetX();
    $y = $pdf->GetY();
    $w = 20;
    $h = 20;

    $cx = $x + $w / 2;
    $cy = $y + $h / 2;
    $cw = $w / 2;

    $start = 0;
    $end = $start + 255;

    $pdf->Circle($cx, $cy, $cw, $start, $end, 'D');
}

function drawBezier($pdf, $reverse)
{
    $x = $pdf->GetX();
    $y = $pdf->GetY();
    $w = 20;
    $h = 20;

    $cx = $x + $w / 2;
    $cy = $y + $h / 2;
    $cw = $w / 2;

    if (!$reverse){
        $start = 0;
        $end = $start + 255;
    }else{
        $start = 285;
        $end = 180 + 360;
    }

    if (!$reverse){
        $sign = 1;
        $point = $end;
    }else{
        $sign = -1;
        $point = $start;
    }

    $ax = $cx + $cw * cos(deg2rad($point));
    $ay = $cy - $cw * sin(deg2rad($point));


    if (!$reverse)
        $aw = $x + $w - $ax;
    else
        $aw = $ax - $x;
    $ax1 = $ax + $aw / 2 * $sign;
    $ax2 = $ax + $aw * $sign;
    $ax3 = $ax + $aw * $sign;
    $ay1 = $ay;
    $ay2 = $ay + $w / 3;
    $ay3 = $ay + $w / 2.5;
    $p1 = [$ax1, $ay1, $ax2, $ay2, $ax3, $ay3];

    $pdf->Polycurve($ax, $ay, [$p1], 'D');
}

$pdf = new TCPDF();
$pdf->AddPage();
$pdf->SetFont('genshingothic', '', 8);

$color = [255,100,100];

$pdf->SetFillColor(...$color);
$pdf->SetDrawColor(...$color);

$pdf->Cell(0, 0, '円弧を描く', 0, 1);
drawCircle($pdf, false);

$pdf->SetY($pdf->GetY() + 30);

$pdf->Cell(0, 0, '円弧を反転してもう一つ描く', 0, 1);
drawCircle($pdf, false);
$pdf->SetX($pdf->GetX() + 20);
drawCircle($pdf, true);

$pdf->SetY($pdf->GetY() + 30);
$pdf->Cell(0, 0, 'ベジエ曲線を描く', 0, 1);
drawCircle($pdf, false);
drawBezier($pdf, false);

$pdf->SetY($pdf->GetY() + 30);
$pdf->Cell(0, 0, '円弧とベジエ曲線を反転して描く', 0, 1);
drawCircle($pdf, false);
drawBezier($pdf, false);
$pdf->SetX($pdf->GetX() + 20);
drawCircle($pdf, true);
drawBezier($pdf, true);



$pdf->Output();

Debian busterにmxeをインストールしてQtCreatorで使う

Mastering Qt 5 の Chapter 2 で、いきなり windows.h が要求されて詰まった。ハードル高くない?スルーしてLinuxのコードの部分だけやればいいのかもしれないけど、もやもやする。
そもそもQtを使う人は様々なOSで使いたいわけだから、最初の方のchapterに複数OSの対応を書いておくのが良いということかな。
あまり詳しい説明はないままWindowsLinuxMac用のコードが書いてある。それぞれのPCで各自環境構築はやってねということか。OSはあることはあるけど、いちいちソースを移動するのは面倒だよね。それぞれに開発環境を入れないといけないし。
仕方がないのでwindowsバイナリをコンパイルできるように設定する。


まず、mxeを取ってきておもむろにmxeをmakeする。

git clone https://github.com/mxe/mxe.git
cd mxe
make qt5

missing ... と表示された場合は、足りないものを https://mxe.cc/#requirements を見ながらインストールする。
(aptコマンドをコピペしても良いが、一応一つ一つ確認しながらインストールしたいので)

今の自分の環境だと以下のパッケージが必要だった。

apt install bison flex gperf intltool-bin libtool lzip

あとは

make -j10 MXE_TARGETS='x86_64-w64-mingw32.static' qt5

してしばらく待つ。
結構時間がかかる。(-j オプションと64bitターゲット指定を忘れないように)


# 4/27 追記
身内だけで使うならこれでもいいけど、公開するなら

make -j10 MXE_TARGETS='x86_64-w64-mingw32.shared' qt5

shared の方が良いかも。
まあ配布するなら、配布用バイナリはWindowsコンパイルする方が良いのかもしれないが。

あと、そもそもqtcreatorをwineで入れる方法があるようだ。
Cross compiling Qt application for Windows on Linux with dynamic linking - Stack Overflow
こっちはちょっと面倒かな。それなら仮想マシンWindowsにqtcreatorを入れる方が楽な気がする。


# 4/28 追記
mxe で sharedのバイナリは起動できなかった。

This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.
Unhandled exception: unimplemented function KERNEL32.dll.RaiseFailFastException called in 64-bit code (0x000000007bc5d60c).
Register dump:

というエラーが出て、ログには

wine: Call from 0x7bc5d60c to unimplemented function KERNEL32.dll.RaiseFailFastException, aborting
wine: Unimplemented function KERNEL32.dll.RaiseFailFastException called at address 0x7bc5d60c (thread 0009), starting debugger...

というエラーが出ている。他にも

0025:err:winediag:xrandr12_init_modes Broken NVIDIA RandR detected, falling back to RandR 1.0. Please consider using the Nouveau driver instead.
002b:fixme:dbghelp:elf_search_auxv can't find symbol in module

のエラーが出ていた。NVIDIAのエラーは、正常に動く場合でも出てる。

仕方がないので普通にWindowsVisual Studio 2019 と Qt を入れた。

# 追記ここまで


コンパイルが終われば、QtCreatorの「オプション」「Kits」の設定で
コンパイラ
 Cコンパイラ:mxe/usr/bin/x86_64-w64-mingw32.static-gcc
 C++コンパイラ:mxe/usr/bin/x86_64-w64-mingw32.static-g++
CMake
 mxe/usr/bin/x86_64-w64-mingw32.static-cmake
Qtバージョン
 mxe/usr/x86_64-w64-mingw32.static/qt5/bin/qmake
キット
 上記設定したものを指定

適当にMinGWとでも名前を付けてPATHを手動追加する。
コンパイラのABIは x86 windows msys pe 64bit にする。


chapter 02 をある程度進めて、Windows版はビルドしてから wine64で立ち上げる。
これでいいのかな?とりあえずCPU WidgetLinux版もWindows版もDebian上で確認できた。



Macはクロスコンパイルは無理っぽいのであとで実機でやる。

Qtをやってみてる

Qt Creatorをインストールする。

apt install qtcreator

他にも何か入れたかもしれない。Qt Creatorを使わずに qmake だけ入れたこともあって、そのときのライブラリが残っているのかもしれない。忘れた。

デバッグでエラーが出るのでpythongdbが使えるようにしておく?

The selected build of GDB does not support python scripting.
It cannot be used in Qt Creator.

デバッグ実行するとこのエラーが出る。
gdbは入っているのになと思っていたら、gdb-minimalではダメで

apt install gdb

する必要があったようだ。

Debugger does not start. GDB not support Python scripting. | Qt Forum
python-dbg は要らなかった。


そうすると起動はしたけどwarningが出る。

&"warning: GDB: Failed to set controlling terminal: \343\203\207\343\203\220\343\202\244\343\202\271\343\201\253\345\257\276\343\201\231\343\202\213\344\270\215\351\201\251\345\210\207\343\201\252ioctl\343\201\247\343\201\231\n"
printf "warning: GDB: Failed to set controlling terminal: \343\203\207\343\203\220\343\202\244\343\202\271\343\201\253\345\257\276\343\201\231\343\202\213\344\270\215\351\201\251\345\210\207\343\201\252ioctl\343\201\247\343\201\231\n" | nkf    
warning: GDB: Failed to set controlling terminal: デバイスに対する不適切なioctlです


c++ - Setup GDB with QtCreator - Stack Overflow
ターミナルで実行すれば解決するみたいだけど、いちいちターミナルを開くのは見づらいかもしれない。
とりあえずスルーで。


これでMastering Qt 5を写経してみている。

Amazon | Mastering Qt 5: Create stunning cross-platform applications using C++ with Qt Widgets and QML with Qt Quick, 2nd Edition | Lazar, Guillaume, Penea, Robin | Tutorials

英語だけれど意外と読める。
英語の児童小説は1ページも読めなかったけれど、プログラムの本なら読めるというのが新感覚。
時々英語のAPIドキュメントとかチュートリアルとか見てるおかげ?文章の意味が分からなくてもプログラムの意味は分かるのが大きいのかも。Dockerのセットアップとかが英語で書かれていても、読むというよりはざっと眺める感じだし、この本もそれに近い。重要な概念の説明っぽい箇所は意味を調べつつ読んでるけれども。
Google翻訳アプリのカメラでリアルタイム翻訳が、ちょっと惜しい感じ。文章を上書きしないで単語の説明を出してほしいところだけど、カメラが揺れるし使いやすくするのは難しいのかも。
今はChapter 01 をやったところ。