2025年10月5日日曜日

Haskell入門 型以外の安全性の確保 P286

 actionIO :: IO a -> IO a
actionIO action = do
  mv <- newEmptyMVar    --スレッド間通信用の空のMVarの箱をつくる
  _tid <- forkIO $ do           --スレッドをたてて
    result <- try action         --actionを実行 成功ならRight 値  例外なら Left eを返す
    putMVar mv result         --結果(Either SomeException a)をMVarに入れる
  result <- takeMVar mv    --メインスレッドでMVarから結果を受ける。入るまで待つ動作。
  case result of                       つまり、actionが終わるまで待って結果を受け取る
    Left e  -> throwIO e
    Right r -> return r

 forkIOでスレッドをわけると、普通はその中で起きた例外が親スレッドに伝わらない。
でも、この関数では、例外をつかまえ MVarを通して親スレッドに返している。

久しぶりに室内MLAアンテナでDX

 18MHzバンドで久しぶりDXが開けていた。ゼヤ(Asiatic Russia)の局長さんとFT8で交信できた。5Wの室内アンテナで届いたので、コンディションがよかったようだ。

 FT8でオンエアしている局の密度をみると、やはり、欧州、日本、北米が目立っているのがわかる。

2025年9月30日火曜日

Haskell入門 モナドによるDSLの実現 ~operational~ P274

型の変換を細かく追って、整合性を確認はしてみましたが、けっこう込み入っていて、それに気を取られると全体の構造がわかりにくいので、おおまかにとらえることも大事かと思いました。runSalesTという関数が、再帰を使って、レシピを読み取っていく構造になっているようです。

 その際、ポイントが :>>=という関数のようです。 (ひとつの命令):>>= (それに続く残りの命令) この残りの命令である右辺がkとなっていて、 これが次のrunSalesTへ再帰で渡される構造になっているようです。そして、次のrunSalesTで、また先頭の(ひとつの命令)が取り出され、それに応じて枝分かれした処理が行われ、...これを繰り返していく。

  ちょうど、リストを順に処理していく再帰と同じ構造のようです。func( [a:k])=(何らかの処理   func(k)) の形。

 簡単なoperational使ったDSLのコードをChatGPTに頼んで作ってもらいました。

sample.hs

.yamlに- operationalを追加必要し stack runghc  sample.hsで7と表示します。
あるいはstack runghc --package operational sample.hsでもいいようです。

2025年9月29日月曜日

Haskell入門 Applicativeスタイル パーサー P251

 既習のモナドを組み合わせると、パーサーもこんなふうになるというよい例だと思います。ただ、かなり複雑にはなってきていますが。 20170401のような日付のパーサーの例です。

data YMD = YMD Int Int Int deriving Show    
countRead :: Read a => Int -> Parser Char -> Parser a
countRead i = fmap read . count  i  
ymdParser :: Parser  YMD                                                                    
ymdParser = YMD <$> countRead 4 digit <*> countRead 2 digit <*> countRead 2 digit 

1. YMD は関数:データコンストラクタ YMD は、単なる値ではなく
 YMD :: Int -> Int -> Int -> YMD という「Intを3つ受け取って YMD を返す関数」でもある。
2. countRead の役割:countRead n digit は「数字を n 個読み取り、それを整数として解釈する」パーサーである。
 例えば: countRead 4 digit :: Parser Int(年を読む)
      countRead 2 digit :: Parser Int(月や日を読む)
3. <$> で最初の適用:(<$>) :: (a -> b) -> Parser a -> Parser b
 a = Int  b = Int -> Int -> YMD  関数 f = YMD 値 Parser a = countRead 4 digit
 したがって:  YMD <$> countRead 4 digit :: Parser (Int -> Int -> YMD)
 ここで、Parser の中には「年をすでに受け取った部分適用関数 YMD y」が入っている。
4. <*> で次々に引数を渡す:次の <*> により
  Parser (Int -> Int -> YMD) <*> Parser Int :: Parser (Int -> YMD)
  さらにもう一度 <*> を使うと:
  Parser (Int -> YMD) <*> Parser Int ::  Parser YMD
※イメージ表せば
 <$>:外の関数を箱に入れる    <*>:箱の中の関数に箱の中の値を渡す 

2025年9月27日土曜日

Haskell入門 Readerモナド P186 

 testrun env = ( `runReader` env)  $ do ③
  cons1 <- consume
  cons2 <- consumue
  constOthers <-  local (\e->e{powSaveMode =True}) $ do
     cons3 <- consume              ①           ②
     cons4 <- consume
     return (cons3 + cons4}
  return (cons1+cons2+consOthers)

local :: (r->r) -> Reader r a -> Reader r a    
                 ①    ②          ①がモード切替の関数 ②do以降が対象

 「最初2回通常モード後半2回がSaveモードで電力を測定したものをすべて合計」というレシピをつくり(③のdo以降)それに、testrunのenvでその都度PowerEnvを入れてやる。そんな、流れでしょうか。

 (   ) `runReader` env   は runReader   (    )   env    ということ?のようです。(   )が未適用のところでここに③のdo以下のレシピがくるような、部分適用かと。

Haskell入門 P179 Alternative型クラスでMaybeを使う

let assocs = [("hiratara",39),("shu1",0),("masaharu",32)]
do age <- lookup "honma" assocs <|> lookup "hiratara" assocs   ①
      guard $ age < 20 ②
      return age 
②が、分かりづらかった。

guard True = pure ()  つまり Just ()
guard False= empty つまり Nothing

 「do式でMaybeを使った場合、①や②のどの行であっても、Nothingが出たらそこでストップして、Nothingを返す(つまり全体の結果がNothing)。最後まで行けばJust ageですが。」 
 この「どの行であっても」がポイントになりそうです。これがあるからモナドが便利なんだろうと思いますが。この性質は、以前にも目にしていて、そのときは理解していたつもりでしたが、しばらくすると忘れてしまうようで、きちんとした言葉でメモしておくことも大事かもしれません。

2025年9月26日金曜日

Haskell 代数的データ型、レコード記法(data)とnewTypeについて

 〇 data BookInfo = Book Int String [String] deriving (Show)
型コンストラクタ名 =データコンストラクタ名 フィールドの型1 フィールドの型2,,,
(新しい型の名前=型構成子)   (値構成子)      (構成要素1,2,,,,)
データコンストラクト名を関数として扱い、Int,String,,,という型の引数に適用するとBookInfoの型の新しい値を生成できる   myInfo=Book 978~ "〇〇" ....

〇 data Bool = False | True   代数的データ型  パターンマッチで使用

〇 レコード記法:フィールドに名前
data Employee = NewEmployee { employeeAge :: Integer , employeeName::String,...}
 employee = NewEmployee { employeeName="~ ,em~   }
   employeeName employeeで名前にアクセスできる アクセサは関数

〇newType NewtypeInt  = N Int deriving (Eq,Ord,Show) N 1< N 2は Ordで可、+不可
    data DataInt = D Int  deriving (Eq,Ord,Show)と比べると制限がある
 ・ちょうど一つのフィールドのみ可   newType Okay=ExactlyOne Int
 ・パラメータは問題なし  newType Param a b = Param (Either a b)
    レコード記法も可  ただしフィールド無は不可 複数の値構成子も不可

newType NTIndexed a =NewNTIndexed { unNTIndexed :: (Integer,a)} deriving Show
 NewNTIndexedとunNTIndexedが、NtIndexed aと (Integer,a)を行き来するための関数として使えるのが便利らしい。

※なお、newtype Ph a = MkPh (Int,a)として、:t MkPhとしてみたらMkPh :: (Int, a) -> Ph aとなりました。 外側から見ると Ph aでも、中身は (Int,a)ということになります。内部構造 (Int, a) は隠して、簡潔に見せたいという考え方?

Haskell 型クラスについて

 Haskell入門とRealWorlHaskellあたりを参考に型クラスについて まとめたみた
Java         Haskell
インターフェース   型クラス     型がもつべきメソッドを規定
具象クラス      インスタンス   規定されたメソッドの実装
インスタンス     値        個々のデータ

class BasicEq  a  where    ← BasicEqという型クラスを宣言
   isEqual ::  a -> a -> Bool          インスタンスの型はaで表現 
   isEqal x y = not (isNotEqual x y)    ←このようなディフォルト実装あれば、インスタンス
   isNotEqual :: a -> a -> Bool    で繰り返しの実装を省力化できる
   isNotEqual x y= not (isEqual x y)

 この型クラスのインスタンスである型は この型クラスの中で定義されている関数を実装しているならどんな型でもいい。 この型クラスでは1つだけ関数を定義している。

  関数の型を並べるときにその名前(a)をインスタンスのクラスを表すのに使うことが必要。
:t isEqualを確認すると 
  isEqual ::  ( BasicEq a ) => a->a-> Bool  
 これは、「任意の型aに対して aがBasicEqのインスタンスである限り、isEqual は 型aのパラメータを2つ取り、Boolを返す」 

isEaqualの定義は
instance BasicEq Bool where
  isEqual True True = True
 isEqual False False =True
 isEqual _  _  =False
instance BasicEq Color where
  isEqual Red Red = True
 .....  のようにインスタンスの種類を増やせる

2025年9月22日月曜日

Haskell入門 P390

 サーバー側のビルドがうまくいかず、苦労していたけれど、deriveJSONエラーが、どうやら、上にあるものですでに処理されているものを、下の方で使うということらしい。順序を次のように変えてみたら、うまくビルドできた。最近の仕様は厳しくなっているのかもしれない。

deriveJSON defaultOptions ''Money
deriveJSON defaultOptions ''AuctionItemId
deriveJSON defaultOptions ''ItemId
deriveJSON defaultOptions ''NewItem
deriveJSON defaultOptions ''Item
deriveJSON defaultOptions ''Term
deriveJSON defaultOptions ''NewUser
deriveJSON defaultOptions ''UserId
deriveJSON defaultOptions ''Inventory
deriveJSON defaultOptions ''AuctionRequest
deriveJSON defaultOptions ''AuctionServerRequest
deriveJSON defaultOptions ''AuctionException
-- deriveJSON defaultOptions ''UUID
deriveJSON defaultOptions ''User
deriveJSON defaultOptions ''AuctionItem
deriveJSON defaultOptions ''AuctionResponse
deriveJSON defaultOptions ''AuctionServerResponse

あとは、client.hsについては、以下行の削除したら、なんとかビルドが通った。
--import qualified System.Console.Haskeline.MonadException as Haskeline (catch)

--instance Catch.MonadThrow (InputT IO) where
--    throwM e = liftIO $ throwIO e
--instance Catch.MonadCatch (InputT IO) where
--    catch act handler = Haskeline.catch act $ \e ->
--        if isSyncException e
--            then handler e
--            else throwIO e

あとは、サーバ側のMain.hsがサンプルにはなかった?ようなので追加し、.cabalもそれに合わせてmain-isを修正すると、すべてビルドを終えることができた。

※サーバへのクライアントのアクセスも、GUIでなく、すべてコマンドライン操作なので、昔のパソコン通信のようなもので、久々に懐かしい感覚を思い出しました。

2025年9月18日木曜日

クラウド経由の家計簿データ保存

  これまで、raspiにテキストデータとして、外出中の買い物データを保存して、自作家計簿ソフトでインポートするようにしていたけれど、実際、旅行中など使ってみると、削除や編集の機能もないと不便だった。そこで、sqlite3を使って、raspiのbottleはWebAPI形式にしてみた。

 スマホはjavaでCRUDができるようにしたが、これが一番時間がかかった。完全にMVCに則ってコーディングするため、メンテはしやすいだろうけど、最初はなかなか大変。画面の該当行を長押しで、編集・削除もできるようにしたところ、けっこう便利になった。一般のクラウドでとなると、維持費もまあまあかかると思うので、個人利用なら、やはり、raspiが一番の節約になりそう。

 raspiは、省エネのわりに、SSRと組み合わせてタイマー機能、タッチパッドPC起動、ネットラジオ録音とさまざま活用できて手放せない存在になっている。

2025年9月6日土曜日

gitのリモートへのクローン(備忘録)

 githubにも、方法が表示されますが、念のため手順をまとめてみました。最近は、公開鍵も使うようになっているので。

・ mkdir myproject  cd myproject      git init
・ echo "# My Project" > README.md      git add .
  git commit -m "first commit"
・リモート側で空リポジトリ作成
   (例: GitHubで myproject を新規作成)

・ ssh-keygen -t ed25519 -C "your_email@example.com"(または -t rsa でもOK)>公開鍵 (~/.ssh/id_ed25519.pub) を GitHub の[Settings] → [SSH and GPG keys] → New SSH key に登録(アカウントのほうのSettingsで)>(~/.ssh/config)につぎのようにして443ポートを使うように指定、ルータのフィルタリングを通過させたい場合必要

Host github.com
  Hostname ssh.github.com
  Port 443
  User git

・ssh -T git@github.com で接続確認

・git remote set-url origin git@github.com:ユーザー名/リポジトリ名.git

   によりパスワード不要になる。

・ローカルのブランチ名が master の場合は git branch -M main で統一

・git push -u origin main

※毎回のプッシュを簡単にするためスクリプトを作ったら、けっこう便利。
git add .
git commit -m "commit_$1"
git push -u origin main
gitPush.sh に第一引数としてコメントも入れられるよう、上記のスクリプトにした


2025年9月4日木曜日

wslでtomcat10

root権限で ネット上の情報を参考にwslでtomcat10を動かしてみた。

wget https://downloads.apache.org/tomcat/tomcat-10/v10.1.44/bin/apache-tomcat-10.1.44.tar.gz
tar -xzvf apache-tomcat-10.1.44.tar.gz
apache-tomcat-10.1.44/bin/startup.sh で起動
apache-tomcat-10.1.44/bin/shutdown.sh で停止
localhost:8080でWindowsからも見ることができた。
pico apache-tomcat-10.1.44/conf/tomcat-users.xml
 <role rolename="manager-gui"/>
  <user username="admin" password="pass" roles="manager-gui"/>

myapp/  <---プロジェクト名
 ├─ WEB-INF/
 │    ├─ classes/
 │    └─ lib/
 └─ src/
           └─ HelloServlet.java という構成にして
つぎに、jspはwebappsの下のプロジェクト名直下において、表示なるか確認
index.jsp
http://localhost:8080/myapp

そして、servletは プロジェクトのディレクトリで
javac -d WEB-INF/classes  src/HelloServlet.javaで、コンパイルすると、表示が確認できる。
ライブラリ指定必要な時は、javac -d WEB-INF/classes -cp /home/user/tomcat10/apache-tomcat-10.1.44/lib/servlet-api.jar src/HelloServlet.javaのように-cpオプションで指定

HelloWorld.java
http://localhost:8080/myapp/hello

2025年9月1日月曜日

Haskellのテストの仕様について

注意点が2つほど  このへんは、ChatGPTでもなかなか気づけなかった。まだ、試行錯誤できる人間よりは、AIは不利かもしれない。

・テストファイルひとつならSpec.hs のままでも OK
しかし、複数だと、Main.hsとしないとだめな仕様になっているようです。このへんは、気づきにくいところ。

・cabalファイルにはpackage.yamlの設定が反映されるけれど、個別のファイルの設定は反映されず、globalスコープのdependencies:のところに、依存関係を記述しないとだめだった。

dependencies:
  - base >= 4.7 && < 5
  - text
  - bytestring
  - aeson
  - sqlite-simple  などのように
  

2025年8月31日日曜日

Haskell Scottyをapache転送で外向けに

 内部LANでのサーバ稼働はうまくいったので、外向けに以前設定したraspi2のapache転送をそのまま利用することにした。ただ、これも一筋縄でいかなかった。(クッキーを利用する場合は、転送処理は難しいようなので、あくまで簡易的な利用にとどめる予定)

・sudo nano /etc/apache2/sites-available/default-ssl.confで、以下のような行を追加

ProxyPass /hs http://追加ラズパイのローカルipアドレス:3000

これで、外部からsslでhttps://ホスト.ドメイン/hsでアクセスすると追加raspi3の3000ポートに転送される。

他に苦労したところでは、

・raspi間の転送がうまくいくように、ルータのipフィルタリングの許可設定を追加

・webアプリに0,0,0,0でリスンするようにコードを書き換える必要があった。そうしないと、なぜか転送がうまくいかなかった。なお、キャッシュが残っているとうまくいかないこともあるので、ビルド、ブラウザは適宜、キャッシュクリアの操作が必要かも。あと、warpを.yamlに追加する必要があった。

  app/Main.hs


※追記:アパッチ転送で注意が必要なのは、/の転送は最後にしないとだめということ。ルートの後に、転送の設定をしてもそれは、無視されるようです。順序が大事なようです。

2025年8月30日土曜日

Haskell入門 Webアプリ(覚書)

 10章のWebアプリを読み終えたのはいいけれど、Spockは実際にビルドしても、依存関係のエラーでかなり面倒なことが判明。いろいろ、バージョンを変えて調整したが、ネット上の情報も少なく諦める。せっかくのSpockの勉強も無駄?になったようで、ちょっと回り道してしまった感じもある。

 とりあえず、Scottyあたりが、比較的メンテもされているようなので、そちらを試すことにした。まずは、簡単なコードをしばらくしまい込んでいたraspi3で、動作確認をしてみた。

・stack new sample           cd sample
・package.yamlについて:   executables>sample-exe>dependenciesに
    - sample
    - http-types
    - aeson
    - scotty
・Main.hsについて
 
・stack build       stack install
    stack exec sample-exe   でhttp://raspiのアドレス:3000/でアクセス

これで、ようやく動作の確認はできた。
    ちなみにコード修正後はstack runで  ビルドと実行ができるようだ
     stack cleanでビルド結果を削除(再ビルドしたいとき)

2025年8月25日月曜日

Haskell入門P368 ルーティング、本体

HaskellでSpockを使ったルーティングは、慣れるまで大変そうですが、無駄のない構成という感じがします。

authHook:ログイン認証
・ctx <- getContextについて:ユーザーからのリクエスト関連情報が、ある時点で箱にはいっていて、そこから、とりだしたのがctxへいく、というイメージ。
・mUser <- fmap wrsesUser readSessionについて:オブジェクトにフィールド名を関数として適用すると、該当フィールドの要素を返す仕組みになっている(P96).
readSession :: WRAction (SessionVal UserSession):現在のリクエストに紐づく セッション情報 を取得する関数 data UserSession = UserSession { wrsesUser :: Maybe User....から、取り出しmUserに

spockApp:ルーティング
・外側のprehookは認証なし、内側はauthHookで認証 うまく階層的に配置してわかりやすい構造
・get,postの別やURLと対応するActionが並べられている。
prehook (return emptyContext) $
 ├─ prehook authHook $ do
 │    ├─ get root
 │    └─ post "new_record"
 ├─ post "register"       ← authHook は適用されない
 └─ post "login"          ← authHook は適用されない

weightRecorderMiddleware:WAI ミドルウェアとして起動するための処理
runWeightRecorder cfgで定義されているように、runSpockで呼ばれる。
    ここで、spock spCfg spockApp にreturnがないが、do内のモナド計算なので不要

2025年8月24日日曜日

Haskell入門 P364 グラフ表示部の実装

ここも、分かりにくいところがあり、ChatGPTの助けを借りるが、ほぼ完ぺきにわかりやすく説明してくれるので、非常に助かる。
・weightGraphValueはMustacheへ渡すValueを返すのが役目。

・liftIO $ mapM flat wrsについて:flatでweightはそのまま使い、日付のみ"%m/%d"形式に変換し、 flat::WeightRecord-> IO (String,Double) よりmapM flat wrsはIO[(String,Double)]を返す。このへんは、mapMの働きを覚えてないと使えない、正直言って、前に学習(P174)したことを忘れており、復習して気づいた感じ。IOを含んでいるのでliftIOでWRActionモナドに持ち上げている。

・let wrss = groupBy ((==) `on` fst) . sortBy (compare `on` fst) $ flatWrsについて:
 sortBy (compare `on` fst) は、fstの日付でソートしている。groupBy ((==) `on` fst) は同じ日付のものをまとめるらしい。つまり[ [("08/20",60.5), ("08/20",61.0)],  [("08/21",60.8)]となる。wrssはリストのリストになります。(==)にそういう機能があることなど、非常に細かいところだけどChatGPTがしっかり解説してくれたのには、驚いた。
 本によれば、onは第二引数の関数を適用してから、第一引数の2項演算子を適用するという働きらしい。これと、上記の説明をみて、ようやく意味が理解できた。

・groupToValueについて:  :: [(String, Double)] -> Valueである。
   dt = head $ map fst gr→ グループの日付を取り出す(全部同じなので先頭でOK)。
   wt = avg $ map snd gr→ 複数記録がある場合は平均を取る。
 なるほど、うまくでてきています。これを使えばgroupToValue [("08/20",60.5),("08/20",61.0)]→ object ["day" ~> "08/20", "weight" ~> 60.75]となります。

2025年8月22日金曜日

Haskell入門 P354 各種操作の実装

whenはtrueならアクションを実行、startViewは実行したら次の行にはいかない。
runSqlite で (Connection -> IO m)を引数にとるため、insertUserの部分適用をうまく使っている。 (insertUser  :: IConnection c  => NewUser -> c -> IO Integerなので)
このへんがHaskellの簡潔さにつながっているのだと思う。

Haskell入門 P352 コントローラの開発

P352「type WRApp ctx= SpockCtxN ctx WRConnection WRSession WRState
type WRAciton = SpockActionCtx WRCibtext WRConnection WRSession WRState
WRAppとWRActionはWeightRecoder アプリケーションで利用するモナドです。
先ほどコンテキストにはWRContextを使うものとしてsrc/Web/Core.hsに定義しました。
 しかし、/のコンテキストは必ず()になるため、WRContextは適しません。そのため、ルーティングに用いるWRAppでは()にも対応できるよう、ctxを指定できるよう定義しています。一方で、ロジックを実際に記述するWRActionのコンテキストは、()を受け取らないようにできるため、型変数ではなくWRContext固定としています。このため、ルーティングの根本でコンテキストをWRContextに設定する必要があります。」

 この部分がよくわからなかった。どうやら、型変数ctxの例がWRContextという型などであるようだ。WRappとWRActionは厳密に関連しているのかと思ったけれど、必ずしもそういうことではなかったよう。WRAction が型変数をとらない理由は、型を固定して型安全にしたいからのよう。WRApp はルートや URL ごとに ctx が異なる可能性があるので型変数で柔軟にしている。そういうことらしい。WRappは設定データだから、いろんなデータを設定できる方がいい、でもWRActionは、安全性が求められるのである程度使える設定データは制限したい、そんな感じだろうか。

2025年8月21日木曜日

Haskell入門 P347 ユーザー登録のためのINSERT

 仕組みがわかりにくかった。

そこで、次のような質問をChatGPTにしてみた。
Q1:piNewUserは、どういうふうに使い方をするのですか?
Ans: let ins = HRR.typedInsert User.tableOfUser piNewUser がポイント
typedInsert :: Table User -> Pi User NewUser -> Insert NewUser
第1引数 User.tableOfUser → 「Userテーブルそのもの」
第2引数 piNewUser → 「NewUser から User のカラムに対応付ける射影」

let ins = HRR.typedInsert User.tableOfUser piNewUser
    u'  = u { nuPassword = dec hashed }                ins は INSERT 文のひな型

DHR.runInsert conn' ins u'     と実行すると、
u' :: NewUser の値(ユーザー名とハッシュ済パスワード)を
piNewUser に従って User テーブルの name, password カラムに対応付け
実際に DB へ INSERT

Q2: 要するに、手順を最初にくみたてて、最後に、runInsertで実行するのですね。
Ans:  はい、その理解でバッチリです!
  まとめると:
   piNewUser で「どう射影するか」を定義(テーブルと型の橋渡し)。
   typedInsert で「INSERT文のひな型」を作成。
   runInsert で「実際の値」を流し込んでDBに送信。

※ Haskellは、手続き型のように「値を受け取りながら逐次SQL発行」というやり方はあまりせず、まず「設計図を型安全に作る値を流し込む」という 宣言的な流れ
 型安全性が高い:SQL文やカラム名の間違いをコンパイル時に検出できる
 SQLインジェクション対策が組み込みである:値を直接文字列連結しないので安全
 トランザクション管理が明確:withTransaction の中で設計図に値を流し込むので、途中でエラーがあればロールバック可能
 コードの再利用性が高い:射影(piNewUser のような定義)を作っておくと、INSERTや  SELECTで同じマッピングを使い回せる
 などの利点があるらしい
Q3:makeRecordPersistableDefault ''NewUser はどういう意味?
Ans:NewUser を Relational-Record の「DBに入出力できる型」として登録する宣言、Template Haskell によって必要な型クラスインスタンスを自動生成している
「この型をSQLに流せるようにしますよ!」と最初に宣言しておく儀式 みたいなもの
Q4:Template Haskellとは?(P340にも説明はありますが)
Ans:「Haskellプログラム自身を生成する Haskellプログラム」を書ける
Q5:自動生成ですね。DjangoやRuby on Railsなどでも、DB初期化で似たような処理あったような気がしますね.
Ans:まさに DjangoRuby on Rails と同じ発想です。
Rails/Django = 実行時に自動生成・マッピング
Haskell(TH) = コンパイル時に自動生成・型安全にマッピング
Q6:user <- DHR.runQuery conn q name >>= DHR.listToUniqueのDHR.listToUniqueの働きは?
Ans:[] の場合 → Nothing を返す、[x] の場合 → Just x を返す、[x, y, ...] の場合 → エラーか Nothing を返す。「DB側にユニーク制約があるとは限らない」設計を想定。
Q7:checkHash user | validated = Just user | otherwise = Nothingの意味は?
Ans: if ... else if ... else if ... elseのイメージで、最初にtrueになったところが checkHash userの値。ガードの機能(p62で既習)

2025年8月18日月曜日

Haskell入門 P326 処理のまとめ

stack install で実行ファイルを作り、動作確認してみた。これも、一筋縄ではいかず、Windowsだと、文字エンコードなど配慮しないとうまくいかなかった。エラーメッセージ見たり、ChatGPTに聞いたりしながら、なんとか解決。
 
echo [{"age":"aa","name":"名前","telnumber":"abcdef"},"age":"bb","name":"abcd","telnumber":"jkl"}] > test.json
としてファイルをつくってから
hjq-exe "{\"name\":.[1].name,\"telnumber\":.[1].telnumber}" test.json
としたらうまくいった。ハイフンがあるとうまくいかず、カットする必要あった。なかなか、大変だった。


Hjq.hs
・note:MaybeをEitherに変換    decode:ByteStringをValueに変換
 first:Rightはそのまま、Leftに関数を適用


Haskell入門 P323 テストようやく終了

 本のままでは、動かず新しいバージョンに合わせた修正が必要だった。

よくわからないところを確認してみた。
Spec.hs
・applyFilterTestについて
   ^?  指定データを1部取り出したいとき使用(lens)
 nth 2 リストの3番目要素取り出し
   fmap Right (testData^?key "string-field") だと  fmap::(a->b)->f a->f bでみると
 (a->b)がRight  f aがMaybe Value   f bがJustつまりMaybe(Either e Value)
 これにRightが適用なるようだ

Parser.hs
・queryArrayの jqQueryParser `sepBy` schar ',' について
  sepByは中値だが、2引数関数で schar ','を区切りとして jqQueryParserでパース

Query.hs
・applyFilter (JqField fieldName n) obj@(Object _)
  = join $ noteNotFoundError (T.pack fieldName) (fmap (applyFilter n) (obj ^? key (fromText(T.pack fieldName))))  について
   obj@(Object _)はObjectとか確認し、その値をobjとして利用可能
 joinはMaybeをはずすため

・executeQuery (JqQueryObject o) v
   = fmap (Object . KM.fromList . fmap (\(k,v) -> (Key.fromText k, v)))
    . sequence ②
    . fmap sequence ①
    $ fmap (fmap $ flip executeQuery v) o  について        
    左辺の(JqQueryObject o) vは{"name":.name,"age":.age}::[(Text,JqQuery)]
    右辺の fmap $ flip executeQuery v のflipは引数を逆順にして、部分適用を意図した 通りにするために使う。(flip executeQuery v) q == executeQuery q v 
  fmap (fmap $ flip executeQuery v) は、内側のfmapで( , )への処理を、外側のfmapで上の関数使って[  , , ]への処理を行う。
  sequenceは①で Eitherを外に出していく [(either Text (Text,Value))]
        ②で Ehter Text [(Text,Value)]にする

   パーシングは、なかなか複雑

2025年8月16日土曜日

Haskell入門 P318

 スペースがある場合のパース

Spec.hs

Parser.hs
本ではscharが標準である?のかもしれないが、新しいライブラリにはないようで、作る必要があった。ChatGPTから作ってもらう。ついでに、以下のような説明もしてもらうが、 Megaparsec自体なじみがないので、難しい感じが。理解できるようになるためには、もう少し時間がかかりそう。
1. sc :: Parser ()
sc = L.space space1 empty empty
目的:空白やコメントをまとめてスキップする
L.space は Megaparsec が提供する空白スキップ用パーサ。
引数:
space1 → 1個以上の空白文字(' ', '\t', '\n')をスキップ
empty → 行コメントの始まり(今回は使わないので空)
empty → ブロックコメントの始まり(今回は使わないので空)
つまり、この sc は「空白があるところを自由に無視する」パーサです。

2. lexeme :: Parser a -> Parser a
lexeme = L.lexeme sc
目的:トークンを読んだあと、自動で空白をスキップする
使い方:
lexeme L.decimal    -- 数字を読み取ったあと、空白を消費
lexeme (some letterChar)  -- 識別子を読み取ったあと、空白を消費
イメージ:
入力: "123   "
lexeme L.decimal → 123 を返す、空白も消費ない場合は、後ろの空白が残り、パースが失敗しやすくなります。


3. symbol :: String -> Parser String
symbol = L.symbol sc
目的:記号(キーワードや演算子)を読んだあと空白をスキップ
例:
symbol "."  -- ドットを読み取り、空白も消費     symbol "["  -- 左ブラケットを読み取り、空白も消費      内部的には lexeme (string s) と同じような働きです。


4. schar :: Char -> Parser Char
schar c = lexeme (char c)
目的:単一文字のトークンを読み取り、空白をスキップ
文字 . や [ ] の後ろに空白があっても問題なくパースできる
例:
schar '.'  -- "." と後ろの空白をまとめて消費      schar '['  -- "[" と後ろの空白をまとめて消費


Haskell入門 P316 修正コード

 P316のコードが動作しなかったので、ChatGPTから同等のものに書き直してもらったのがこちら。新しいライブラリMegaparsecを使うといいらしい。

Parser.hs

・type Parser =Parsec Void String     :Voidがエラーの型(ない) Stringが入力の型
   Parser   a     =   Parsec Void String  a
 限られた使い方 aが結果     Parsecのほうがカスタマイズしやすいといえる
・try paraseIndex <|>   try parseField  <|> pure JqNil    で<|>は左が失敗したら右ためす
   tryは 失敗しても入力を消費せず次を代わりにためす (入力を巻き戻す)
・some letterChar     :  some 1回以上繰り返し  letterChar a~zA~Zのパース
・option JqNil  (char '.' *> filterRest <|>  parseField)
      option::  a -> Parser a -> Parser a    
         ディフォルト値->試すパーサ->失敗したら ディフォルトaを返す
      char '.' *> filterRest    :  .を消費し、filterRest返す
       ここではtry不要。すでにfilterRestの中でtryは終えているので

Haskell入門 P316

 このへんのパーサ扱ったところはかなりややこしくなっている。
結局、コードが古くてうまく動作せず、別のライブラリを使うことになった。

でも、いちおう、古いほうも、コードをいろいろ確認してみた。
・showParserResult $ parse (jqFilterParser <* endOfInput) s `feed` ""は
showParserResult $ (parse (jqFilterParser <* endOfInput) s) `feed` ""と同じ意味
<* endOfInput 該当しないところはパースせず `feed` ""はパース停止の決まり文句のようなものらしい

・fmap pack $ many1 (letter <|> char '-' <|>..... digit())について
many1 は  letter <|> char '-' <|> ... <|> digit は 1文字分のパーサー
many1 (...) はこれを 1回以上繰り返して成功した結果をリスト [Char] にまとめる
fmap pack によって [Char]Text に変換  
        letter :: Parser Char なので   many1 letter :: Parser [Char] になります。
箱(Parser)はそのままで 中身 [Char] が Text に変わる

・JqIndex <$> パース1 <*> バース2 の意味について復習
<$>で最初に部分適用の関数ができて、その関数のまだ未適用な部分に<*>を順次適用していくしくみ
 パーサの場合、関数がつぎつぎと、無限に適用できる引数があるようなもの。その場合は、つねに、部分適用な関数のままでJqIndexが残ることになるといえそう。再帰状態になるのかも。

2025年8月13日水曜日

Haskell入門 P315 パーサの作成

 本のコードのままではだめで、けっこう厳密さが要求された。バージョンの違いか?

・型のスコープとインポートは明示的に
・Text と String は別物    (T.pack ".")で Textに
・コンストラクタもエクスポートリストで見えるようにする     (..)など利用
・stack.yaml / package.yaml の依存関係はビルド対象ごとに確認(ライブラリも指定)
といった注意も必要なことがわかる。

ちなみに修正したものは
Spec.hs

Parser.hs

Hjq.hs

package.yaml

Haskell入門 P314 HUnitによる自動テスト

 テストファーストの考え方をとっているようで、最初にテストの方法がでていた。
ただ、本の内容はバージョンが古くそのままでは動かなかった。
・Test suite not yet implementedとでるので
  package.yamlにtest:hjq-test:dependencies:のところに - HUnitを追加
  Spec.hs の先頭にimport Test.HUnit 追加
・文字エンコードでエラーがでるので
  Dosプロンプトのショートカットに cmd /k "chcp 65001 &&  cd パス"のように、事前にエンコード切り替えをいれる
・runTestTTの型が新しいバージョンでは 返り値が IO Countsというものらしいので
 以下のようなコードにするらしい。

import Test.HUnit
import System.Exit (exitFailure, exitSuccess)
main :: IO ()
main = do
    counts <- runTestTT $ "Test1" ~: 1 + 1 ~?= 2
    if errors counts + failures counts == 0
        then exitSuccess
        else exitFailure

これで、ようやく、うまく動作した。

2025年8月10日日曜日

Haskell入門P286

以下のコードの目的は、子スレッドのエラーがメインスレッドに影響しないようにしたいため、隔離しているようです。
actionIO ::  IO a -> IO a
actionIO  action = do
 mv <- newEmptyMVar :: IO (MVar (Either SomeException a))
 _tid <- forkIO $ do
  result <- try acttion
  putMVar mv result
 result <- takeMVar mv
 case result of
  Left e -> throwIO e
  Right r -> return 

1. actionIO :: IO a -> IO a
IO aのアクションを引数にとり、結果もIO aとして返す関数です。引数のactionを別スレッドで実行し、その結果を取得します。

2. mv <- newEmptyMVar :: IO (MVar (Either SomeException a))
新しい空のMVarを作成します。MVarはスレッド間で値のやり取りができる同期変数です。
型は MVar (Either SomeException a) としています。Either SomeException aは、「例外が起きたか」あるいは「正常に値が得られたか」を表すために使います。

3. _tid <- forkIO $ do ...
forkIOで新しい軽量スレッドを作り、その中でactionを実行します。
forkIOはスレッドIDを返しますが、このコードでは使わないので_tidとして無視しています。

4. result <- try action
tryは、例外が発生する可能性があるIOアクションを安全に実行し、成功時はRight a 例外発生時はLeft SomeExceptionの形で結果を返します。

5. putMVar mv result
mv(空のMVar)に結果を格納します。これでメインスレッドはこの結果を待つことができます。

6. result <- takeMVar mv
メインスレッドで、mvに格納されるまで待ちます。forkIOの中の処理が終わりputMVarが呼ばれるまでブロックされます。

2025年8月9日土曜日

Haskell入門 P285 MVarによるスレッド間の通信

 ChatGPTもlevel5になって、さらにバージョンアップした感じです。

本を読んでも、なかなか理解しにくかったところも、ChatGPTにコードを解説してもらうとすぐに理解できたので、質問者のレベルに合わせて説明できる能力に驚いています。


・m <- newEmptyMVar newEmptyMVarで空のMVarを作成  これはスレッド間の通知用の「信号」として使う予定
 ・forkIOで新しい軽量スレッドを作成。
  この新しいスレッドの中で、myThreadIdでスレッドIDを取得し、コンソールに表示。
  ・putMVar m ()で空のMVarに通知(単なる空のタプル () を入れているだけ)を送る。
  ・メインスレッドはtakeMVar mで、MVarに値が入るのを待つ(ブロックされる)。
  子スレッドが2秒後にputMVarで通知すると、この待機が解除される。       
  ・メインスレッドは、子スレッドが終わったことをMVar経由で待機している。
   子スレッドは、重たい処理の後にMVarに通知し、メインスレッドに処理完了を知らせる。

 ※ putMVar m () は、普通は MVar aにaを入れる(例えばMVar IntにIntを入れるように)。今は()を入れているが、これはCでいえばVoid scalaでいえば、Unitみたいなもの。

2025年7月2日水曜日

今年から市民農園再開

 しばらくやめていた市民農園に再挑戦で、今年は小玉スイカの空中栽培も試してみた。植え付け時は、炭疽病を心配し、乗り越えたかと思えば、今度はウリハムシ、そして受粉がなかなかうまくいかないとか、いろいろありましたが、今のところ、それほど雨に悩まされることなく病気の症状もおさまっているようです。このまま梅雨明けしてくれるといいのですが。

 
 虫や病気の心配も少なくネットもいらず、片付けも楽、意外と手間がかからず、コンスタントに収穫できるのがつるなしインゲンです。
 

 ※小玉スイカは、受粉日のタグをつけて35~40日後に収穫したところ、ちょうどよい甘さになりました。2025/7/25

2025年6月26日木曜日

ロープの結び方

 畑作業でもよく使うので、男結び等について 図にまとめてみました。(トラックロープの結び方は南京結びともいうようです。) 他にも、巻き結び、もやい結び、結束結び、自在結び、ひとえつぎ結び等、覚えておくと便利なようです。

GASではJSON.stringfyをつかったほうがいいようです

 畑の収穫記録をGASで管理(収穫の個数を記録しその都度、合計の確認)しようかと、ChatGPTにコード頼んでみたけど、なかなか一筋縄ではいかない。AIにより、だいぶコーディングは楽になっているが、やはり、ある程度やりとりしながら、人間側が修正してあげないと動かなかった。

 最初、ChatGPTはサーバ(GAS)からのレスポンスデータを単純にオブジェクトで渡すコードを提示してくれたけれど、それではうまくいかず、やはり、JSON.stringfyを使わないとだめだった。

※ MEMO欄を追加しました。2025/6/27

2025年6月2日月曜日

ガロア理論入門(アルティン) 問11-3(2)

P117 p素数 Φp^n(x)=Φp(x^(p^(n-1)))が既約であることを示す問題の途中の変形がしばらくわからず悩む。

Φp(x^(p-1)+(pでわりきれる項)+1)={x^(p^(n-1))+(pでわりきれる項)}^(p-1)+(pで割り切れる項)+pとなる理由

∵前問で①Φp(x+1)=((x+1)^p-1)/((x+1)-1)=x^(p-1)+Σk=1~(p-2) pCk x^(p-k-1)+p が言えていたので、これを使うとよいことにきづく。今の場合、X=x^(p-1)+(pでわりきれる項) としてこれを①のxをXで置き換えるとうまくいくようです。

 前問をつかっているということに、きづかなかったためしばらく悩みました。よく読むと問題にも(1)と同様にして解く と書いてあるのを見逃していました。

2025年5月17日土曜日

アルティンのガロア理論入門 P106

 途中の式変形で、すこし手こずる。x^(q^n)-α^(q^n)-(x-α)を(x-α)で割ってから、x=αを代入した式がq^n*α^(q^n-1)-1となることを示したい。ChatGPTにすぐ頼ってしまったが、いまいちすっきりしない。

 与式をf(x)とすると  f(x)=(x-α)h(x)とみなし h(x)=f(x)/(x-α)だから、h(x)=f'(x)とみなせるからという説明だった。たしかに、x→αでは、解析的には微分だが、、、、(自分には高度すぎる?)。

 と、少し間をおいてあらためて考えると 単純に式の割り算をすると、

Σ i=0~q^(n-1)     α^i*x^(q^n-i-1)  -1  となることに気づく。これにx=αを入れると、結論が言えるようだ。自分としては、こちらのほうが納得できる。

2025年3月29日土曜日

モナドの活用

 Haskell入門 を久しぶりに読み直してみた。P168にMaybeモナドについて少し復習してみた。

getItemWithMonad menu category name =do
  subMenu <- lookup category menu
  price <- lookup name subMenu
  return (category,name,price)

途中、Nothingを記述しなくても自動的に、該当しなければ、最後はNothingになる。うまくできていると思う。
これを、手続き型言語で、実現しようとすると条件分岐に頼らざるを得ないけど、その必要もない。
このへんが、関数型言語のモナドの強みなのかもしれない。

追記>試しに、ChatGPTで、ELMのMaybeを使うコードに変換してみた。ELMはモナドをサポートしていないため、少しコード量が増えてしまうようだ。
type alias Menu = Dict String (Dict String Float)
getItemWithMonad : Menu -> String -> String -> Result String (String, String, Float)
getItemWithMonad menu category name =
    case Dict.get category menu of
        Just subMenu ->
            case Dict.get name subMenu of
                Just price -> 
                    Ok (category, name, price)
                Nothing -> 
                    Err "Item not found"
        Nothing -> 
            Err "Category not found"

2025年3月19日水曜日

1960年代からの国債残高の増加率をグラフにしてみた。

 

 国債の増え方が話題になっているようなので、実際のところどうなのか、公式データを加工して処理してみました。この計算の仕方が、実際の増え方を表しているかどうか議論のあるところかもしれませんが、ある程度の参考にはなりそうです。
 縦軸は前年比の増加率(%)です。ちなみに、1873年 0.234億円 2024年 11053645億円ということのようなので この期間の1年あたりの平均増加率を計算すると (11053645/0.234)^(1/(2024-1873))=1.124   ということで、ほぼ12%程度です。
 1985年あたりから10%切るようになっていますね。それ以来ほとんど、(1998年以外は)平均に届いてないようです。

追加:ちなみに、近年のものですがアメリカも調べてみました。日本のように、5%切ることは少ないような感じです。


2025年3月14日金曜日

ルベーグ積分 理論と計算手法 P199

 補題9.13(2)でまた、つまづいてしまった。仕方なく、だめもとでChatGPTに頼ってみたら、きちんと回答してくれた。AIの力も凄いものだと驚きました。ここ何か月間の間にパワーアップした?

 質問は 「ルベーグファトウの定理を使い、∫A  max k≦n fk μ ≦ ν(A)  から ∫ sup n∈N  fn  μ ≦ ν(A)をいえますか?」としました。 回答に少し補足を入れてまとめてみた。

∵) gm=max k≦n fk  (これは増加列 ①)として   定理を適用すると

∫A  lim inf n→∞  gn  μ ≦ lim inf n→∞ ∫A gn μ

左辺は  ∫A sup n∈N fn μ  ②                       ↓ここで①を使う

また sup n∈N inf k≧n gn  = lim n→∞ inf gn=lim n→∞ gn  =sup n∈N fn   これが②の理由

∫A gn  μ ≦ ν(A)  で n→∞ ならば  lim inf n→∞ ∫A gn μ≦ ν(A)

ゆえに 結論  ∫A  sup n∈N fn μ ≦ ν(A)

定理に必要な優関数がないので、質問したら、優関数はf1(x)という回答。専任の家庭教師がついているような感覚になる。

2025年2月20日木曜日

Σsinkx/k の求め方で苦戦

 ルベーグ積分 理論と計算手法 P183 例8.9(1)で、


Σsinkx/k=(π-x)/2  (2π>x>0)になる理由がわからず、苦戦。

log(1-z)を微分し -1/(1-z) = Σz^n なので

積分して  log(1-z)=-∫Σz^n dz =-Σ∫z^n dz  = - Σ z^(n+1)/(n+z) 

 = - Σ z^n/n  (Σが n=0からとなっていたのをn=1からとすることで変形)

これを使うと

Im(log(1-e^ix))=-ΣIm(e^ixk)/k   = -Σ sin kx /k

これは arg(1-e^ix)=-(π-x)/2 (0<x<2π) でもある

∵)log(re^ix)=logr+log(e^ix)=logr+ix

∵) 1-e^ix=(1-cosx)-isinx      r=√((1-cosx)^2+sin^2x =2sin(x/2)

 -sinx / 2sin(x/2)=sinΘ なら Θ=-(π-x)/2になる。

∵) 2sin(x/2)sin((π-x)/2) = sinx  このへんは地道に三角関数の計算を確認すると

  確かにそうなる。それにしても計算が大変だった。

フーリエ級数使うともう少し楽に計算する方法もありそうなので、そちらも試してみたい。

追記(2/22):その後、ディリクレージョルダンの定理(P182)を使うと、計算できることが確認できた。

f(x)=-x/(2π)+1/2*sgn(x) として bk=1/kとできるので、これを定理に適用するといいようだ。最初うまくいかなかったのは、ちょっとした計算ミスのためだった。だいぶ遠回りしてしまった。

ついでに、(2)も解いていみた。

x∈(-2π,2π)を x'=x/2∈(-π,π)とすると  2Σsin2kx'/2k=Σsin2kx'/k=-x'+π/2*sgnx'

(1)より 2(-x/2+π/2*sgnx)=2Σsinkx/k=2( Σsink2x/2k   + Σsin(2k-1)x/(2k-1) )

=-x+π/2*sgnx  + 2*(求めたい式)となるが、これを整理すると

 求めたい式= Σsin(2k-1)x/(2k-1)=π/4*sgnxとなる。

PC用CW発振器(ツインT型)作成

 peanutでは、PCでCW交信ができるようになっているが、そのための発振器を作ってみた。最初、2SC1815と2SA1015を組み合わせた簡易的なものを試したが、周波数がうまく変えられなかったので、ツインT型を試したらうまくいった。入力はUSBへの変換アダプタを使うことにしている。

以前作成したPICによるキーヤーのコレクタ部分に上記回路のアースを接続することで、うまく動作させることができた。

追記:送信練習用に、以前作ったモールスデコーダにもつなげるようにしてみた。ただ、音量が不足していたので、こちらのサイトを参考させていただき、一石のアンプを追加した。マイクを通さず、デコーダにつなぐと、雑音の影響がなくなり、ほぼ100%の認識率になるようです。送信練習にはとても役立ちます。


2025年1月28日火曜日

JMoocに少し挑戦してみた

  無料の大学講座JMoocに興味を持ち、少し勉強してみた。修了証もらうまで行く人が1割ということらしい(無料ということとも影響している?)。単元テストは計算力を必要とされる問題が多く時間がかかった。

 抵抗2つ、コンデンサ2つを並列や直列組み合わせた回路で、抵抗値が流れる電流に影響しなくなる条件を求めよという問題や、交流回路の複素計算など、ちょっとしたミスがあるとなかなか解けなかったりする。久しぶりに、繁分数の式を変数に置き換えたり、〇.〇×10^△の形にして、少しでも計算が楽にならないか考えたり、LTSPiceなどPCの便利な計算に慣れてしまった昨今、逆に脳にはいい刺激になるかもしれないと思った。後半の三相交流の話になってくると、電力なども含めて考えると複雑になってきた。

 電気回路に続いて、電子回路も受講、電気回路が物理に近い内容で計算中心だったが、電子回路のほうほ工学的内容だった。後半は集積回路に組み込むために容量の大きなコンデンサを使わない回路の工夫について説明があった。MosFETなどHFリニアアンプの製作にもいろいろと役立つ情報があった。とりあえず、2つの講座ともなんとか修了できた。

2025年1月26日日曜日

中足骨骨頭痛

  以前から、歩くと靴の中に小石があるような痛みがあり、でも確かめると何もないという症状ががあり気になっていたが、よく調べると中足骨骨頭痛という症状らしい。足のアーチが弱くなるのが原因らしい。歩き方も踵に重心が来るように歩くといいらしい(足を前に出そうとするのでなく、上にあげてそのまま真下に落とすという感覚)。この症状が出ているときは、ウオーキングもほどほどにしたほうがいいようだ。

2025年1月25日土曜日

USB電力不足

  以前から気になっていたけれど、デスクトップPCのバスパワーUSBハブが電力不足のためか、マウス以外つなげない状態。試しに、ケーブルの途中に三又状態にして、余ったUSB充電アダプタとUSBプラグを使って5Vを追加できるようにしてみたらうまくいった(若干コネクタ部分の接触が気になる感じはあったが、PCからの経路すべてに接点復活材をスプレーしたらだいぶ改善された)。

 本来であれば、セルフパワーのUSBハブを購入すればいいのだろうけど、数千円はするので、余り物でなんとかできたので、少し節約はできたかもしれません。(というか、これ以上、5V電源アダプタは増やしたくなかったというのもありますが)

2025年1月24日金曜日

ChatGPTは計算が苦手?

 ルベーグ積分理論と計算手法P159の計算をChatGPTに聞いてみたが、書籍の答えと一致しない。

∫(0,+∞) (y/(y^x+1) - y/(y+1)^2) λ(dy) が確かlog2と答えた。

正しくは、たぶん

[ log(y^2+1) /2 -  (  y/(y+1)  -  ∫1/(y+1)λ(dy) ) ] (0,∞)  かと思う。

                                ↑部分積分

上記で計算すると確かに1になるので。

ChatGPTも、人間と同じようにまだ間違うこともあるようだ。

ChatGPTはLLMなので、どちらかというと帰納的推論?なのかもしれない。対して、wolframなどは、演繹的推論らしいけれど。。

それにしても、AIで、プログラミングもそうですが、いろんな独学もだいぶやりやすくなったと思う。

2025年1月19日日曜日

PIC12F1822(CPS)の実装

 ブレッドボードでの実験はなんとかうまくいきそうなので、実装してみた。これからケースに収める予定。静電容量計測のケーブルもコネクタで取り外しできるようにしてみた。

 前回、arduinoで灯油残量センサを作ったときは、誘導雷で壊れたこともあるので、灯油タンクまでの配線が長くなるので、サージアブソーバのようなものもつけないとだめかもしれない。
追記:CPSを測定しているが、灯油タンクに、導線2本入れて試しているものの、なぜか、測定値が安定しない。原因はまだ不明。おそらく、タンクまでの配線の容量が大きく、また、温度等でRC発信周波数が安定しないためかと思われる。

2025年1月18日土曜日

PIC12F1822でCPSの実験(AQM1602Aを使用)

  静電容量の変化をCPSという機能で確認できるらしいので、実験してみた。以前、灯油タンクの残量検知を超音波センサとarduinoで試したがうまくいかなかったので、静電容量を使ってできないか、そのうち試してみたいという目的もあります。

・カウントのオーバーフローするとマイナス表示になるようなので、delay関数の値を調整する必要があった。

・RA3を入力で使えるようにしたかったので、#pragma config MCLRE = OFF  #pragma config LVP = OFF とするとよいという情報がネット上にあった。(LVPの必要性はよくわからない?が)

・RA5のLED出力とRA3のスイッチ入力は、とくに機能は実装していないけれど、今後の何らかの機能が必要になったときのために、コードで反応を確認してみた。

・少しわかりにくかったのは、秋月電子のLCD(AQM1602A)のコントラストがコードで設定するようになっていたこと。以下の(1)は72の2の部分が4bitで (2)は0101の4bitで5で 0100が4ですが、その下位2bitのみ関係しているので、00→01→10→11 で54~57の範囲になります。
  LCD_cmd(0x72);  //Contrast set                 下位4bits...(1)
    LCD_cmd(0x54);  //Power/ICON/Contrast Control  下位2bits...(2)  
   (2)と(1)あわせて 2^6=64 stepの範囲から指定するという意味だった。((2)のほうが上位)   ※5Vだと上記ぐらいの値だけれど、3.6v(ニッケル水素充電池3本)だと、(1)0x73 (2)0x55ぐらいが必要だった。



2025年1月17日金曜日

デスクトップPCをraspi経由で電源On

 PCの電源Onは、USB、内部タイマー、キーボード等々いろいろあるけれど、スリープ状態にするため、夜間勝手にアップデートなどでOnなるのも気になるので、完全にraspiで操作できるようにしてみた。

 PCの電源は、導通でOnなので、例によってトランジスタ2sc1815を使った。raspiのGPIOは出力が3.3Vで、前回ブルーレイな場合と同じ回路(raspiのGPIOからトランジスタ経由でPCスイッチへ)にしてみたらうまくいった。最初、マザボピンの分岐コネクタに手持ちのピンでつないだが、接触不良起こしやすいようだったので、はんだ付けにする。PCの余分な穴にDC電源用のソケットをグルーガンで固定して、raspiとの接続ケーブルを取り外しできるようにした。(raspi側にも同様にソケットをつける。)

2025年1月14日火曜日

極限の計算のコツ

ルベーグ積分理論と計算手法P152 あたりで、ちょっとした極限の計算が分からない。最近は、どうにも解決できそうにないときChatGPTに聞くことにしている。

 1<α<3   lim x→0   (sinyx-yx/(1+x^2))/αx^α が、さらっと0になることが書いてあるが、理由がわからなかった。

 こういうときは、いろいろ展開を使うといいらしい。

   sin yx = sin0 +yx*cos0 -(yx)^2*sin0/2! -(yx)^3*cos0/3!.....

        =yx-(yx)^3/6   ①

       1/(1+x^2)=1+(-x^2)+(-x^2)^2+(-x^2)^3+.....

        なので  yx/(1+x^2)=yx(1-x^2+x^4...)=yx-yx^3+yx^5.....  ②

   与式=(①-②)/αx^α=(y-y^3/6)x^3... /αx^α だが

     α<3より  3-α>0 で x→0なら  確かに x^(3-α) → 0がいえるようだ。(最初、ChatGPTも途中の計算間違っていたので、再度、確認したら正しい説明をしてくれた。やはり、ChatGPTにとっても少し難しくなるとミスもしやすくなるんだろうか?)

 学生時代にこういった勉強も多少はやったはずだけど、ほとんど記憶に残っていない、やはり使わないと忘れてしまうようです。


2025年1月13日月曜日

raspiで秋月のSSR操作

 raspiにSSRを組み合わせてタイムスイッチつくってみた。

秋月電子のSSRキット(ゼロクロス、25A)を使った。100Vなので絶縁には細心の注意が必要。たまたま、先日、分解廃棄したDVDレコーダの中からヒートシンクを取り出していたので、早速活用。

フォトカプラを使っているようで、入力で15mmA消費とあったので、raspiのGPIOでも許容範囲であり、そのままつなげている。

ChatGPTに助けてもらい、以下のようなスクリプトを使ってうまく動作させることができた。(rapiでは、OSの中にすでに以下のようなスクリプトが使える環境がある)

*****ssr_control.sh**********
#!/bin/bash
GPIO_PIN=3  # GPIO3
if [ ! -d /sys/class/gpio/gpio$GPIO_PIN ]; then
    echo "$GPIO_PIN" > /sys/class/gpio/export
    echo "out" > /sys/class/gpio/gpio$GPIO_PIN/direction
fi
if [ "$1" == "on" ]; then
    echo "1" > /sys/class/gpio/gpio$GPIO_PIN/value
elif [ "$1" == "off" ]; then
    echo "0" > /sys/class/gpio/gpio$GPIO_PIN/value
else
    echo "Usage: $0 {on|off}"
    exit 1
fi

cronを設定すれば、タイムスイッチとして使える
30 5 * * * cd /home/pi && ./ssr_control.sh on
30 6 * * * cd /home/pi && ./ssr_control.sh off

2025年1月11日土曜日

PIC16F628AでUART for Dfplayer(備忘録)

古い型のPICはDfplayerとのUART通信の資料が少なく苦労した。
arduino系と違ってPICはよりハードに近いため、通信のタイミングなどより細かい調整が必要だとわかった。外部発振子が必要だという情報もあったが、内部発振でも可能なような気もしたので、少し頑張ってみた。

確認したこと:各ポートの入力、出力の設定(TRISA,TRISB)、 内蔵発振4MHzが使えること(#define _XTAL_FREQ 4000000と PCON   = 0b00001000)、UART関連設定(TXSTA,RCSTA) Config系(FOSC = INTOSCCLKで内蔵発振を指定) SPBRG=25(4MHzと9600Bps、非同期、ハイスピードなどの条件から計算できることが、データシートに書かれている)
ここまでは、自力でなんとかできたが、問題は、UARTの送信関数

最初は
void dfply(char cmd, char parameter) {
    while (TXIF == 0);
    TXREG = 0x7E;
  ...........
としたが、これだと、データが完全に送信しないうちに次を送ろうとしてだめらしい。ChatGPTから、以下の方法を教えてもらい、試したらうまくいった。
void dfply(char cmd, char parameter) {
     while (!TXIF); 
    TXREG = 0x7E;  
    while (!TRMT);
  ....    
   
 以下のようなコードでフォルダ、ファイル番号をスイッチ長押しでカウントして取得し、Mp3を指定して再生することはうまくできた。確認のため、フォルダやファイル番号をモールス信号をLEDで表示するようにした。 

2025年1月8日水曜日

ルベーグ積分 理論と計算手法 P150

 P150で「  t ∈ R|Z で、Γ(t)Γ(1-t)sinπtが周期1である」の理由が省略されていたので、気になり、考えてみた。
Γ(n+t)=(n-1+t)Γ(n-1+t)=.....=(n-1+t).....tΓ(t)    ①
{1-(n+t)}Γ(1-(n+t)) = Γ(1- {(n-1)+t})
[1-{(n-1)+t}]Γ(1-{(n-1)+t}) =  Γ(1- {(n-2)+t})
......
{1-(1+t)}Γ(1-(1+t)) = Γ(1-t)     
これより、 Γ(1-t)={1-(n+t)}.....{1-(1+t)}Γ(1-(n+t)) ②
①、②より
Γ(n+t)Γ(1-(n+t))=(n-1+t).....tΓ(t)×Γ(1-t)/{(1-(n+t)).....(1-(1+t))}
=(-1)^n  Γ(t)Γ(1-t)
ここで  sinπ(n+t)=(-1)^n sin πtなので
Γ(n+t)Γ(1-(n+t))sinπ(n+t)=(-1)^n  Γ(t)Γ(1-t)  (-1)^n sinπt
=(-1)^2n  Γ(t)Γ(1-t) sin πt = Γ(t)Γ(1-t) sin πt  n∈Zなら これより、周期1を示しているだろうと、考えた。