「const」を含む日記 RSS

はてなキーワード: constとは

2025-11-22

自己放尿たいてい

const NGWORDS = [

'dorawii',

'あおやまちゃん', 'ボスマン',

'エーブイ',// イーブイ

'れめくん', '/boushi_inst',// 電気通信大学れめくん(頻出)

'/asapgoeson', '/gmhtcyznf_abc', '/fft_dareka', '/h2twi', '/kiha2228', '/kqlex1500', '/oreizmmiporin', '/pm95uq', '/reme_kun',// 電気通信大学れめくん

'自己放尿',

];

みんなも各々対策してるのかな?

対策なしだとけっこうしんどいよね…?

ChatGPT すごい

utf8の文字コード構成されたバイナリファイルがある。このファイルの改行の数を数えよ。改行はCRのみとする。このファイルにはCR が必ず含まれており、CRで分割すればマルチスレッドで処理できる。

質問したら、c++pythonコードが出てきた

size_t count_cr_in_file(const std::string& path) {

std::ifstream fs(path, std::ios::binary);

fs.seekg(0, std::ios::end);

size_t size = fs.tellg();

fs.seekg(0);

const size_t thread_count = std::thread::hardware_concurrency();

const size_t chunk_size = size / thread_count;

std::vector<std::future<size_t>> futures;

for (size_t t = 0; t < thread_count; t++) {

size_t start = t * chunk_size;

size_t end = (t == thread_count - 1 ? size : (t + 1) * chunk_size);

futures.push_back(std::async(std::launch::async, [start, end, &path]() {

std::ifstream fs(path, std::ios::binary);

fs.seekg(start);

size_t count = 0;

for (size_t i = start; i < end; i++) {

char c;

fs.read(&c, 1);

if (c == '\r') count++;

}

return count;

}));

}

size_t total = 0;

for (auto& f : futures) total += f.get();

return total;

}

def count_cr(path):

import mmap

with open(path, "rb") as f:

mm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)

return mm.count(b'\r')

c#で書けと指示したところ、

using System;

using System.IO;

using System.IO.MemoryMappedFiles;

using System.Threading.Tasks;

class CountCR

{

public static long CountCRInFile(string path)

{

using var mmf = MemoryMappedFile.CreateFromFile(path, FileMode.Open, null);

using var accessor = mmf.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read);

long fileSize = accessor.Capacity;

int processorCount = Environment.ProcessorCount;

long chunkSize = fileSize / processorCount;

long total = 0;

object lockObj = new object();

Parallel.For(0, processorCount, i =>

{

long start = i * chunkSize;

long end = (i == processorCount - 1) ? fileSize : (i + 1) * chunkSize;

long localCount = 0;

byte b;

for (long pos = start; pos < end; pos++)

{

accessor.Read(pos, out b);

if (b == 0x0D) // CR

localCount++;

}

lock (lockObj)

{

total += localCount;

}

});

return total;

}

static void Main(string[] args)

{

string path = "yourfile.bin";

long count = CountCRInFile(path);

Console.WriteLine($"CR count = {count}");

}

}

というコードが出てきた。

実際はcrlf,lfもあり得るんだが、そこは考慮しなくてもよく、そう考えるとaiはすごいとしか言いようがない。

なお、utf8として成立する単位区切りつつ改行を数える場合、もう一工夫いる。

SJISもできるようにするともっと面倒なことになりそう…)

2025-11-14

ワイが増田の新着ブックマーク独自指定しているNGワード

https://b.hatena.ne.jp/site/anond.hatelabo.jp/?sort=eid

  const NGWORDS = [
    'dorawii',
    'あおやまちゃん', 'ボスマン',
    'エーブイ',// イーブイ
    'れめくん', '/boushi_inst',// 電気通信大学れめくん(頻出)
    '/asapgoeson', '/gmhtcyznf_abc', '/fft_dareka', '/h2twi', '/kiha2228', '/kqlex1500', '/oreizmmiporin', '/pm95uq', '/reme_kun',// 電気通信大学れめくん
    '自己放尿',
  ];

みんなも各々対策してるのかな?

対策なしだとけっこうしんどいよね…?

2025-10-28

増田特定ワードを含む投稿非表示にするJavaScript

// ==UserScript==

// @name はてな匿名ダイアリー特定ワード投稿非表示

// @namespace http://tampermonkey.net/

// @version 0.2

// @description 本文に「dorawii」または「megalodon」が含まれ投稿非表示にする

// @match https://anond.hatelabo.jp/*

// @grant none

// ==/UserScript==

(function() {

'use strict';

const POST_SELECTOR = '.body .section';

// 非表示にしたいキーワード配列

const KEYWORDS = ['dorawii', 'megalodon'];

const posts = document.querySelectorAll(POST_SELECTOR);

posts.forEach(post => {

const textContent = post.textContent || post.innerText;

// いずれかのキーワードが含まれいるかチェック

if (KEYWORDS.some(keyword => textContent.includes(keyword))) {

post.style.display = 'none';

}

});

})();

これはdorawiiもしくはmegalodonを含む投稿非表示にするけど、

const KEYWORDS = ['dorawii', 'megalodon'];の部分を変えたり追加すれば好きな言葉に変えられるよ

2025-10-21

「dorawii」を含む投稿非表示にするJavaScript

dorawiiがタイトルに「dorawii」と入れなくなったので、本文にdorawiiがある投稿非表示にする必要が出てきました。

ただAIに聞いたんだけど、CSSでは無理でJavaScriptならできると言われました。

そのJavaScriptを下に載せます

// ==UserScript==

// @name はてな匿名ダイアリー特定ワード投稿非表示

// @namespace http://tampermonkey.net/

// @version 0.1

// @description 本文に「dorawii」が含まれ投稿非表示にする

// @match https://anond.hatelabo.jp/*

// @grant none

// ==/UserScript==

(function() {

'use strict';

// 投稿全体を囲む要素のセレクタに置き換えてください

// はてな匿名ダイアリー一般的投稿要素のクラス仮定しています

const POST_SELECTOR = '.body .section'; // 例: .bodyクラスの子孫の.section要素

// 非表示にしたいキーワード

const KEYWORD = 'dorawii';

// すべての投稿要素を取得

const posts = document.querySelectorAll(POST_SELECTOR);

posts.forEach(post => {

// 投稿内の本文が含まれる要素(ここでは投稿全体を本文と見なす)のテキストを取得

const textContent = post.textContent || post.innerText;

// キーワードが含まれいるかチェック

if (textContent.includes(KEYWORD)) {

// キーワードが含まれていれば非表示にする

post.style.display = 'none';

}

});

})();

自分iPhoneなので、Makeoverというアプリを入れて、JSの部分に上のを貼り付けてます

なんか複雑だし、もっと良い方法があるなら教えてください!

なんでdorawiiのためにこんなことしなきゃいけないんだよ!迷惑千万

2025-08-23

dorawii@執筆依頼募集中

やべえ。爆速ブクマされるプログラムになった。

async function collectAllUrls(startUrl) {
const urls = [];
let nextUrl = startUrl;

while (nextUrl) {
const res = await fetch(nextUrl);
const html = await res.text();
const doc = new DOMParser().parseFromString(html, "text/html");

const links = doc.querySelectorAll("div.section > h3 > a:first-child");
urls.push(...[...links].map(link => link.href));

const nextLink = [...doc.querySelectorAll("a")].find(a => a.textContent.includes("次の25件>"));
nextUrl = nextLink ? nextLink.href : null;
console.log(nextUrl)
}

return urls;
}

(async () => {
const allUrls = await collectAllUrls(window.location.href);
console.log("総件数:", allUrls.length);

await Promise.all(allUrls.map(url =>{console.log(url);
fetch('https://b.hatena.ne.jp/dorawii_bukuma/add.edit.json', {
method: 'POST',
headers: {
},
body: new URLSearchParams({

'url': url,
'private': '0',
'comment': '[dorawii]わしが書いた',
'post_twitter': '0',
'with_status_op': '1',
'from': 'web-confirm'
})
});
} ));

console.log("全送信完了");
})();

https://b.hatena.ne.jp/site/anond.hatelabo.jp/?sort=eid

↑膨大な数のブクマが19:34分前後登録になってるだろ?

途中でブクマ数増えなくなったんだよね。待機処理つけるべきだったか

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

https://anond.hatelabo.jp/20250823194237# 
-----BEGIN PGP SIGNATURE-----

iHUEARYKAB0WIQTEe8eLwpVRSViDKR5wMdsubs4+SAUCaKmbHwAKCRBwMdsubs4+
SJC0AP0Q7RDjUSe8p2aNNKV0KLhlbhnTY+kD7uuWCS8yLJILDgEA2Sm4b1496jjy
C0ue64hovLwS3C4dcF5r5TBMyfRifw8=
=zIYi
-----END PGP SIGNATURE-----

2025-08-21

dorawii@執筆依頼募集中

自動ブクマするローカルサーバーとかの構成を作った。

ブクマには↓のサブアカ使用

https://profile.hatena.ne.jp/dorawii_bukuma/

はてなサイト側で読み込まれているはずのrksトークンを生成する関数を直接叩く方法がどうしても分からず結局request処理を自分で書く方法ではなく自動UI側の保存ボタンクリックするという無難な方向に落ち着いた。

最初から後者方法をとっていればもっと全然早く作れたのにというは所詮言い訳か。

とにかくスクリプトを公開しておく。

start-server.bat

@echo off
cd /d "C:\Users\user\Documents\jsscript"

:: Nodeサーバーを別ウィンドウで起動
start /min "" node run-batch-server.js

:: Pythonサーバーを別ウィンドウで起動(hatenaserver配下
start cmd /k "" python hatenaserver\server.py

以降はjsscript直下に配置

config.json

{
"username": "",
"password": ""
}
server.py

from flask import Flask, request, jsonify
import json
import os
from hatena_client import HatenaClient
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

config_path = os.path.join(os.path.dirname(__file__), 'config.json')
with open(config_path, encoding='utf-8') as f:
config = json.load(f)

@app.route('/bookmark', methods=['POST'])
def handle_bookmark():
data = request.json
url = data.get("url")
if not url:
return jsonify({"error": "Missing URL"}), 400

client = HatenaClient(config["username"], config["password"])
client.start_browser()

if not client.login():
client.quit()
return jsonify({"error": "Login failed"}), 403

success = client.add_bookmark(url)
client.quit()

return jsonify({"status": "ok" if success else "fail"})

if __name__ == "__main__":
app.run(port=12347)

あとはグリモンユーザスクリプトとして書くやつ

// ==UserScript==
// @name 自動セルクマ送信
// @namespace tampermonkey.net/
// @version 2025-08-07
// @description try to take over the world!
// @author You
// @match anond.hatelabo.jp/*
// @grant none
// ==/UserScript==

(function () {
'use strict';

const url = location.href;
if (!/^https:\/\/anond\.hatelabo\.jp\/\d+$/.test(url)) return;
const editLink = document.querySelector('a.edit');
if (!editLink) {
// 既に編集ページなので処理をスキップ
console.log('編集リンク存在するため、スクリプトを終了します。');
return;
}

fetch('localhost:12347/bookmark', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ url: url })
}).then(r => console.log("通知成功")).catch(e => console.error("通知失敗", e));
})();
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

https://anond.hatelabo.jp/20250821192753# 
-----BEGIN PGP SIGNATURE-----

iHUEARYKAB0WIQTEe8eLwpVRSViDKR5wMdsubs4+SAUCaKb0qwAKCRBwMdsubs4+
SHfiAQDcXmTHBaZ5Zzr1KI/OxZ0xl69oevOdy1FXJYwYvsmo5AD/ZPtZiO1JgTDj
m+27iymlkdzIXOIGWfC82UTr1mJ7EwU=
=YoV+
-----END PGP SIGNATURE-----

2025-07-24

LLMとTypescriptって実は相性悪いよねって話

今時点の使えそうな Sonnet4 を使ってコード生成とか業務でやる時に Typescript は案外うまくいかないことが多い。

UIとかシンプルものであれば結構うまくいくけど、graphql, prisma みたいなところになると、token数すごくなるし結局完成しない。

この辺りが、なんとも小骨がひっかかるからTypescriptの型ってやっぱりあれなのかと思って調べてもらったんだ。

↓↓↓↓↓↓↓

## ソフトウェア工学から見たTypeScriptの3つの根本課題

Web上の専門的な議論論文では、TypeScript課題は主に以下の3点に集約されます。これらはすべて、JavaScriptという土台との不適合性に起因するものです。

1. 不健全な型システム (Unsound Type System)

ソフトウェア工学において、型システムの**「健全性(Soundness)」**とは、「コンパイル時に型エラーがなかったプログラムは、実行時に型エラーを起こさない」という保証を指します。

TypeScriptは、この健全性を意図的放棄しています

設計目標の不在: TypeScript公式ドキュメントには「健全であること」は設計目標ではないと明記されています。これは、JavaScriptとの互換性や開発者利便性を優先した、根本的なトレードオフです。

具体的な不健全さ:

配列の扱い: string 型の変数に (string | number) 型の配列を代入できてしまうなど、配列の扱いに不健全な部分があります。これが原因で、実行時に数値を取り出してしまい、string型のメソッドを呼び出してエラーになる、といったことが起こり得ます

any型と型アサーション: any型の存在や、開発者コンパイラに「これはこの型で間違いない」と強制する型アサーションas構文)は、健全性を根本から破壊する「抜け道」です。

関数引数(Bivariance): 関数引数の型チェックが、他の多くの言語(反変)とは異なり、より緩いルール(双変)になっています。これも実用性を優先した結果、理論的な正しさを犠牲にしている例です。

学術的な観点では、この「不健全さ」はTypeScriptの型システムが持つ最大の弱点と見なされています

2. 構造的部分型(Structural Typing)の罠

TypeScriptは、クラス名などによらず「構造が同じなら同じ型」とみなす構造的部分型を採用しています。これはダックタピングが主流のJavaScript文化に合わせた賢い選択ですが、ソフトウェア工学的にはいくつかの罠があります

意図しない互換性: 全く異なる目的で作られた二つのオブジェクトが、偶然同じ構造を持っていたために、型チェックをパスしてしまうことがあります。これにより、ドメイン業務領域)の意図とは無関係コードが結合してしまい、予期せぬバグを生む原因となります

過剰なプロパティの許容: const config: { port: number } = { port: 8080, host: 'localhost' }; のように、変数経由で代入すると、型に定義されていない余分なプロパティ(host)がエラーになりません。これは仕様ですが、開発者意図しないデータが紛れ込むことを許容してしまい、セキュリティバグリスクに繋がります

これは「TypeScriptの型は、データの『形状』を記述するが、その『意味』や『文脈』を保証しない」という根本的な限界を示しています

3. 「漏れのある抽象化(Leaky Abstraction)」としての本質

Joel Spolskyが提唱した「漏れのある抽象化法則」に倣えば、TypeScriptはまさにその典型例です。

TypeScriptは「静的型付け」という抽象化レイヤー提供しますが、開発者は常にその下にあるJavaScriptの泥臭い現実(undefined, null, thisの挙動など)を意識し続けなければなりません。

抽象化の不徹底: 型を書いているときも、最終的にそれがundefinedになりうることや、thisが何を指すかを常に考えなければなりません。抽象化レイヤーが、下位レイヤーの詳細を隠蔽しきれていないのです。

摩擦コスト: この「漏れ」が、これまで議論してきた「Union地獄」や「境界での型変換の手間」といった、開発上の継続的な摩擦コストを生み出しています

## 結論

ソフトウェア工学的な観点から見ると、Web上の専門家議論は我々の対話結論を強く裏付けています

TypeScript課題は、個別機能の優劣ではなく、「健全性を犠牲にしてでも、JavaScriptという動的な土台との互換性を最優先した」という、その成り立ちそのものにあります。その結果として生じる「不健全さ」「構造的部分型の罠」「漏れのある抽象化」といった問題は、この言語を使い続ける限り、アーキテクトが向き合い続けなければならない、本質的なトレードオフなのです。

2025-07-21

軽い気持ち東京選挙区参政党得票率を線形回帰分析してみた

東京23区と周辺のいくつかの市で、東京選挙区での参政党の得票率がどんな要因と関係しているのか、軽い気持ち回帰分析してみた。

変数の多重共線性とか処理はガバガバなので軽い気持ちで見てほしいんだが、ざっくりまとめると、「大学院卒業者の割合」が高い地域では得票率が低く、「役員割合」が高い地域では逆に得票率が高い、という傾向がありそう。

使ったデータNHKが出している投票所別の得票率。手入力なので誤りがあるかもしれない。

それに、東京都人口統計国勢調査(令和2年の)などから市区町村ごとの属性データをくっつけて、変数を一律で標準化したうえで回帰分析を行った。

都内市区町村のうち、データが揃ってる27地域対象にした(23区町田八王子調布西東京)。

20万人以上の市しか一部のデータが見つけられなくて、そこはごめんって感じ。

ざっくり結果

まず、説明変数11個使って線形回帰分析をしたところ、決定係数は0.83(調整済み決定係数は0.71)だった。何を使ったかは後で。

そこから影響が特に大きそうな4変数(平均年齢、大学院卒業割合役員割合情報通信業割合)に絞って分析し直すと、決定係数は0.73(調整済み決定係数は0.68)になった。

詳しくはこれ

国勢調査は5年に1回しかなくて、最新の結果が令和2年のだった。

でこの4変数回帰係数の絶対値が大きい順に並べる。

  1. 大学院卒業者の割合(-1.30)
  2. 役員割合(+0.87)
  3. 平均年齢(-0.57)
  4. 情報通信業割合(-0.54)

4つの変数関係を見てみると、平均年齢は他の3つの変数大学院卒、役員情報通信業)と負の相関を持っていた(相関係数 < -0.69)。一方、大学院卒業者の割合役員割合情報通信業割合は互いに中程度以上の正の相関(相関係数 > 0.5)を持っており、特に大学院卒と役員の間の相関係数は0.75と大きかった(いずれもピアソン相関)。

ただし、回帰係数を見ると、興味深い違いがある。大学院卒業者の割合、平均年齢、情報通信業割合はいずれも負の係数を持っていて、これらが高いと参政党の得票率は下がる傾向がある。一方で、役員割合は正の係数を持っていた。

得票率と予測値の表
市区町村参政党得票率(NHK予測値_参政党得票率 平均年齢(令和7年1月大学院卒業割合(令和2年国勢調査役員割合(令和2年国勢調査情報通信業割合(令和2年国勢調査
千代田区9.4 9.6 42.69 0.088 0.162 0.115
中央区9.8 9.3 42.17 0.075 0.126 0.135
港区10.1 10.4 43.48 0.065 0.171 0.131
新宿区9.4 9.5 44.08 0.052 0.097 0.129
文京区 7.4 7.6 43.35 0.097 0.098 0.118
台東区1010.1 45.59 0.041 0.109 0.112
墨田区10.1 9.8 44.88 0.035 0.073 0.115
江東区 9 9.4 44.82 0.041 0.069 0.12
品川区 9 8.6 44.34 0.056 0.077 0.143
目黒区 9 9.4 44.88 0.05 0.109 0.137
大田区9.9 9.5 45.67 0.039 0.069 0.105
世田谷区9.9 9.4 45.19 0.047 0.097 0.128
渋谷区109.7 44.8 0.054 0.142 0.152
中野区9.5 9.3 44.57 0.038 0.072 0.141
杉並区 8.5 8.9 45.23 0.047 0.076 0.136
豊島区9.6 9.5 44.05 0.044 0.081 0.132
北区9.2 9.4 45.74 0.036 0.058 0.107
荒川区9.4 9.9 46.23 0.032 0.071 0.096
板橋区9.9 10.0 45.73 0.027 0.059 0.099
練馬区10.3 9.6 45.5 0.034 0.068 0.113
足立区10.5 10.7 46.74 0.017 0.063 0.073
葛飾区1010.4 46.52 0.02 0.061 0.083
江戸川区1110.7 45.09 0.021 0.062 0.085
八王子10.1 9.7 48.31 0.029 0.054 0.054
町田109.5 48.16 0.031 0.058 0.068
調布 8.6 9.4 45.66 0.035 0.06 0.113
西東京9.1 9.5 46.9 0.028 0.055 0.102

感想

雑なモデルなので話半分でね。

データの中身とか、もうちょい詳しく書いとく


出典

分析に使ったデータの出典はこんな感じ。


変数

使用した11個の変数はこんな感じ。


結果についてももうちょい詳しく

statsmodels.api.OLSの結果

                            OLS Regression Results                            
==============================================================================
Dep. Variable:                      y   R-squared:                       0.730
Model:                            OLS   Adj. R-squared:                  0.680
Method:                 Least Squares   F-statistic:                     14.84
Date:                Mon, 21 Jul 2025   Prob (F-statistic):           5.09e-06
Time:                        07:21:02   Log-Likelihood:                -20.653
No. Observations:                  27   AIC:                             51.31
Df Residuals:                      22   BIC:                             57.78
Df Model:                           4                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const       1.277e-15      0.111   1.15e-14      1.000      -0.230       0.230
x1            -0.5743      0.230     -2.493      0.021      -1.052      -0.096
x2            -1.3278      0.204     -6.512      0.000      -1.751      -0.905
x3             0.8670      0.174      4.973      0.000       0.505       1.229
x4            -0.5382      0.169     -3.184      0.004      -0.889      -0.188
==============================================================================
Omnibus:                        2.233   Durbin-Watson:                   2.170
Prob(Omnibus):                  0.327   Jarque-Bera (JB):                1.169
Skew:                          -0.035   Prob(JB):                        0.557
Kurtosis:                       1.983   Cond. No.                         4.48
==============================================================================
説明変数11個でのデータと結果

変数回帰係数
平均年齢(令和7年1月 -0.78
1世帯あたり人口 -0.31
男性率(令和7年1月 0.07
外国人比率(令和7年1月 -0.07
5年間外国人割合変化 0.27
犯罪認知割合 -0.05
大学院卒業者/全卒業者(令和2年国勢調査 -1.77
不詳者/全卒業者(令和2年国勢調査 -0.51
従業上の地位役員割合 1.39
従業上の地位自営業主割合 0.09
産業区分情報通信業割合 -0.53
地域参政党得票率(NHK予測値_参政党得票率 平均年齢(令和7年1月1世帯あたり人口男性率(令和7年1月外国人比率(令和7年1月 5年間外国人割合変化(令和2年から7年) 犯罪認知割合(令和6年件数/令和7年人口大学院卒業者/全卒業者(令和2年国勢調査 不詳者/全卒業者(令和2年国勢調査従業上の地位役員割合(令和2年国勢調査従業上の地位自営業主割合(令和2年国勢調査産業区分情報通信業割合(令和2年国勢調査
千代田区9.4 9.5 42.69 1.75 0.50 0.06 1.22 0.04 0.09 0.36 0.16 0.09 0.12
中央区9.8 9.8 42.17 1.76 0.48 0.07 1.33 0.01 0.08 0.28 0.13 0.08 0.14
港区10.1 10.0 43.48 1.74 0.47 0.08 1.08 0.01 0.07 0.42 0.17 0.10 0.13
新宿区9.4 9.0 44.08 1.52 0.50 0.14 1.12 0.02 0.05 0.39 0.10 0.09 0.13
文京区 7.4 7.5 43.35 1.80 0.48 0.07 1.32 0.01 0.10 0.25 0.10 0.08 0.12
台東区10.0 10.3 45.59 1.58 0.51 0.09 1.21 0.01 0.04 0.36 0.11 0.09 0.11
墨田区10.1 10.1 44.88 1.69 0.49 0.06 1.25 0.01 0.04 0.28 0.07 0.07 0.12
江東区9.0 9.2 44.82 1.84 0.49 0.07 1.23 0.01 0.04 0.27 0.07 0.06 0.12
品川区9.0 8.6 44.34 1.73 0.49 0.04 1.19 0.01 0.06 0.24 0.08 0.07 0.14
目黒区9.0 9.3 44.88 1.74 0.47 0.04 1.19 0.01 0.05 0.35 0.11 0.10 0.14
大田区9.9 9.7 45.67 1.77 0.50 0.04 1.26 0.01 0.04 0.23 0.07 0.07 0.11
世田谷区9.9 9.3 45.19 1.84 0.47 0.03 1.22 0.01 0.05 0.30 0.10 0.10 0.13
渋谷区10.0 9.9 44.80 1.61 0.48 0.06 1.12 0.02 0.05 0.34 0.14 0.12 0.15
中野区9.5 9.5 44.57 1.57 0.51 0.07 1.20 0.01 0.04 0.33 0.07 0.09 0.14
杉並区 8.5 8.9 45.23 1.73 0.48 0.04 1.19 0.00 0.05 0.26 0.08 0.09 0.14
豊島区9.6 9.5 44.05 1.57 0.50 0.12 1.21 0.01 0.04 0.34 0.08 0.09 0.13
北区9.2 9.2 45.74 1.71 0.50 0.09 1.31 0.01 0.04 0.31 0.06 0.07 0.11
荒川区9.4 9.6 46.23 1.77 0.50 0.11 1.19 0.01 0.03 0.29 0.07 0.08 0.10
板橋区9.9 10.0 45.73 1.73 0.49 0.07 1.29 0.01 0.03 0.30 0.06 0.07 0.10
練馬区10.3 9.6 45.50 1.89 0.48 0.04 1.22 0.01 0.03 0.25 0.07 0.08 0.11
足立区10.5 10.6 46.74 1.84 0.50 0.06 1.28 0.01 0.02 0.31 0.06 0.08 0.07
葛飾区10.0 10.5 46.52 1.86 0.50 0.06 1.27 0.01 0.02 0.27 0.06 0.08 0.08
江戸川区11.0 10.8 45.09 1.93 0.50 0.07 1.27 0.01 0.02 0.26 0.06 0.07 0.09
八王子10.1 9.7 48.31 1.96 0.50 0.03 1.28 0.01 0.03 0.21 0.05 0.07 0.05
町田10.0 10.0 48.16 2.06 0.49 0.02 1.44 0.01 0.03 0.17 0.06 0.08 0.07
調布 8.6 9.1 45.66 1.92 0.49 0.02 1.14 0.01 0.04 0.23 0.06 0.08 0.11
西東京9.1 9.2 46.90 2.00 0.49 0.03 1.15 0.01 0.03 0.20 0.06 0.08 0.10



                            OLS Regression Results                          
==============================================================================
Dep. Variable:                      y   R-squared:                       0.833
Model:                            OLS   Adj. R-squared:                  0.711
Method:                 Least Squares   F-statistic:                     6.803
Date:                Mon, 21 Jul 2025   Prob (F-statistic):           0.000472
Time:                        06:53:14   Log-Likelihood:                -14.148
No. Observations:                  27   AIC:                             52.30
Df Residuals:                      15   BIC:                             67.85
Df Model:                          11                                      
Covariance Type:            nonrobust                                      
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const      -5.405e-15      0.106  -5.12e-14      1.000      -0.225       0.225
x1            -0.7820      0.361     -2.165      0.047      -1.552      -0.012
x2            -0.3056      0.355     -0.860      0.403      -1.063       0.452
x3             0.0671      0.270      0.248      0.807      -0.509       0.643
x4            -0.0737      0.213     -0.346      0.734      -0.527       0.379
x5             0.2652      0.168      1.579      0.135      -0.093       0.623
x6            -0.0534      0.246     -0.217      0.831      -0.578       0.472
x7            -1.7650      0.293     -6.018      0.000      -2.390      -1.140
x8            -0.5147      0.379     -1.358      0.195      -1.322       0.293
x9             1.3916      0.348      3.994      0.001       0.649 


  

2025-07-06

Grokが作ってくれたやつ


<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>GrokのPONGゲーム</title>
    <style>
        body { display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #1a1a1a; }
        canvas { border: 2px solid #00ff00; background: #000; }
        #score { color: #00ff00; font-family: 'Courier New', monospace; font-size: 24px; position: absolute; top: 20px; width: 100%; text-align: center; }
    </style>
</head>
<body>
    
0 : 0
<canvas id="gameCanvas" width="800" height="400"></canvas> <script> const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); const scoreDisplay = document.getElementById('score'); // ゲームオブジェクト const ball = { x: canvas.width / 2, y: canvas.height / 2, radius: 10, speedX: 5, speedY: 5 }; const paddleLeft = { x: 10, y: canvas.height / 2 - 50, width: 10, height: 100, speed: 8 }; const paddleRight = { x: canvas.width - 20, y: canvas.height / 2 - 50, width: 10, height: 100, speed: 8 }; let scoreLeft = 0, scoreRight = 0; // キー入力 const keys = { w: false, s: false, ArrowUp: false, ArrowDown: false }; document.addEventListener('keydown', e => { if (keys.hasOwnProperty(e.key)) keys[e.key] = true; }); document.addEventListener('keyup', e => { if (keys.hasOwnProperty(e.key)) keys[e.key] = false; }); // ゲームループ function gameLoop() { // 移動 if (keys.w && paddleLeft.y > 0) paddleLeft.y -= paddleLeft.speed; if (keys.s && paddleLeft.y < canvas.height - paddleLeft.height) paddleLeft.y += paddleLeft.speed; if (keys.ArrowUp && paddleRight.y > 0) paddleRight.y -= paddleRight.speed; if (keys.ArrowDown && paddleRight.y < canvas.height - paddleRight.height) paddleRight.y += paddleRight.speed; // ボール移動 ball.x += ball.speedX; ball.y += ball.speedY; // 壁衝突 if (ball.y + ball.radius > canvas.height || ball.y - ball.radius < 0) ball.speedY = -ball.speedY; // パドル衝突 if ( (ball.x - ball.radius < paddleLeft.x + paddleLeft.width && ball.y > paddleLeft.y && ball.y < paddleLeft.y + paddleLeft.height) || (ball.x + ball.radius > paddleRight.x && ball.y > paddleRight.y && ball.y < paddleRight.y + paddleRight.height) ) { ball.speedX = -ball.speedX * 1.05; // 少し加速 } // 得点 if (ball.x < 0) { scoreRight++; ballReset(); } if (ball.x > canvas.width) { scoreLeft++; ballReset(); } // 描画 ctx.fillStyle = '#000'; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = '#00ff00'; ctx.fillRect(paddleLeft.x, paddleLeft.y, paddleLeft.width, paddleLeft.height); ctx.fillRect(paddleRight.x, paddleRight.y, paddleRight.width, paddleRight.height); ctx.beginPath(); ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2); ctx.fill(); scoreDisplay.textContent = `${scoreLeft} : ${scoreRight}`; requestAnimationFrame(gameLoop); } function ballReset() { ball.x = canvas.width / 2; ball.y = canvas.height / 2; ball.speedX = (Math.random() > 0.5 ? 5 : -5) * (Math.random() * 0.5 + 0.75); ball.speedY = (Math.random() * 4 - 2); } gameLoop(); </script> </body> </html>

 

 

https://anond.hatelabo.jp/20250706011306#

Claudeが作ってくれたやつ



<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>PONG Game</title>
    <style>
        body {
            margin: 0;
            padding: 0;
            background-color: #000;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            font-family: 'Courier New', monospace;
            color: white;
        }

```
    .game-container {
        text-align: center;
    }
    
    canvas {
        border: 2px solid white;
        background-color: #000;
    }
    
    .score {
        font-size: 24px;
        margin: 20px 0;
        letter-spacing: 2px;
    }
    
    .controls {
        margin-top: 20px;
        font-size: 14px;
        opacity: 0.8;
    }
    
    .start-button {
        background-color: #333;
        color: white;
        border: 2px solid white;
        padding: 10px 20px;
        font-size: 16px;
        cursor: pointer;
        font-family: 'Courier New', monospace;
        margin: 10px;
    }
    
    .start-button:hover {
        background-color: white;
        color: black;
    }
</style>
```

</head>
<body>
    
<canvas id="gameCanvas" width="800" height="400"></canvas>
<button class="start-button" onclick="startGame()">ゲーム開始</button> <button class="start-button" onclick="resetGame()">リセット</button>

W/S キーまたは ↑/↓ 矢印キーパドル操作
``` <script> // Canvas要素とコンテキストの取得 const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); // ゲーム状態管理 let gameRunning = false; let animationId; // スコア要素の取得 const playerScoreElement = document.getElementById('playerScore'); const computerScoreElement = document.getElementById('computerScore'); // ゲームオブジェクト定義 const game = { // プレイヤーパドル(左側) playerPaddle: { x: 10, y: canvas.height / 2 - 50, width: 10, height: 100, speed: 5, upPressed: false, downPressed: false }, // コンピューターパドル(右側) computerPaddle: { x: canvas.width - 20, y: canvas.height / 2 - 50, width: 10, height: 100, speed: 3.5, // プレイヤーより少し遅く設定 targetY: canvas.height / 2 - 50 }, // ボールの設定 ball: { x: canvas.width / 2, y: canvas.height / 2, radius: 8, speedX: 4, speedY: 3, maxSpeed: 8 }, // スコア管理 score: { player: 0, computer: 0 } }; // キーボード入力の処理 const keys = {}; // キーが押されたときの処理 document.addEventListener('keydown', (e) => { keys[e.key.toLowerCase()] = true; // ゲームが停止中にスペースキーゲーム開始 if (e.key === ' ' && !gameRunning) { startGame(); } }); // キーが離されたときの処理 document.addEventListener('keyup', (e) => { keys[e.key.toLowerCase()] = false; }); // パドルの移動処理 function updatePaddles() { // プレイヤーパドルの移動(W/S キーまたは矢印キー) if (keys['w'] || keys['arrowup']) { game.playerPaddle.y -= game.playerPaddle.speed; } if (keys['s'] || keys['arrowdown']) { game.playerPaddle.y += game.playerPaddle.speed; } // プレイヤーパドルの画面外移動を防ぐ if (game.playerPaddle.y < 0) { game.playerPaddle.y = 0; } if (game.playerPaddle.y > canvas.height - game.playerPaddle.height) { game.playerPaddle.y = canvas.height - game.playerPaddle.height; } // コンピューターパドルAI処理 // ボール位置を追跡するが、完璧ではない動きを実装 const ballCenterY = game.ball.y; const paddleCenterY = game.computerPaddle.y + game.computerPaddle.height / 2; // ボールパドルの中心の差を計算 const difference = ballCenterY - paddleCenterY; // 反応に少し遅れを持たせる(人間らしい動き) if (Math.abs(difference) > 10) { if (difference > 0) { game.computerPaddle.y += game.computerPaddle.speed; } else { game.computerPaddle.y -= game.computerPaddle.speed; } } // コンピューターパドルの画面外移動を防ぐ if (game.computerPaddle.y < 0) { game.computerPaddle.y = 0; } if (game.computerPaddle.y > canvas.height - game.computerPaddle.height) { game.computerPaddle.y = canvas.height - game.computerPaddle.height; } } // ボールの移動と衝突判定 function updateBall() { // ボール位置更新 game.ball.x += game.ball.speedX; game.ball.y += game.ball.speedY; // 上下の壁との衝突判定 if (game.ball.y - game.ball.radius < 0 || game.ball.y + game.ball.radius > canvas.height) { game.ball.speedY = -game.ball.speedY; } // プレイヤーパドルとの衝突判定 if (game.ball.x - game.ball.radius < game.playerPaddle.x + game.playerPaddle.width && game.ball.x + game.ball.radius > game.playerPaddle.x && game.ball.y + game.ball.radius > game.playerPaddle.y && game.ball.y - game.ball.radius < game.playerPaddle.y + game.playerPaddle.height) { // ボールパドルに当たった位置によって跳ね返り角度を調整 const hitPos = (game.ball.y - (game.playerPaddle.y + game.playerPaddle.height / 2)) / (game.playerPaddle.height / 2); game.ball.speedX = Math.abs(game.ball.speedX); game.ball.speedY = hitPos * 4; // ボールの速度を少し上げる(ゲームをエキサイティングに) if (Math.abs(game.ball.speedX) < game.ball.maxSpeed) { game.ball.speedX *= 1.02; } } // コンピューターパドルとの衝突判定 if (game.ball.x + game.ball.radius > game.computerPaddle.x && game.ball.x - game.ball.radius < game.computerPaddle.x + game.computerPaddle.width && game.ball.y + game.ball.radius > game.computerPaddle.y && game.ball.y - game.ball.radius < game.computerPaddle.y + game.computerPaddle.height) { // ボールパドルに当たった位置によって跳ね返り角度を調整 const hitPos = (game.ball.y - (game.computerPaddle.y + game.computerPaddle.height / 2)) / (game.computerPaddle.height / 2); game.ball.speedX = -Math.abs(game.ball.speedX); game.ball.speedY = hitPos * 4; // ボールの速度を少し上げる if (Math.abs(game.ball.speedX) < game.ball.maxSpeed) { game.ball.speedX *= 1.02; } } // ボールが左右の壁を越えた場合得点処理) if (game.ball.x < 0) { // コンピューター得点 game.score.computer++; updateScore(); resetBall(); } else if (game.ball.x > canvas.width) { // プレイヤー得点 game.score.player++; updateScore(); resetBall(); } } // ボールリセット得点後の処理) function resetBall() { game.ball.x = canvas.width / 2; game.ball.y = canvas.height / 2; // ランダムな方向でボールを発射 game.ball.speedX = (Math.random() > 0.5 ? 4 : -4); game.ball.speedY = (Math.random() - 0.5) * 6; } // スコア表示の更新 function updateScore() { playerScoreElement.textContent = game.score.player; computerScoreElement.textContent = game.score.computer; } // 描画処理 function draw() { // 画面をクリア ctx.fillStyle = '#000'; ctx.fillRect(0, 0, canvas.width, canvas.height); // 中央の点線を描画 ctx.setLineDash([5, 5]); ctx.beginPath(); ctx.moveTo(canvas.width / 2, 0); ctx.lineTo(canvas.width / 2, canvas.height); ctx.strokeStyle = '#fff'; ctx.stroke(); ctx.setLineDash([]); // プレイヤーパドルを描画 ctx.fillStyle = '#fff'; ctx.fillRect(game.playerPaddle.x, game.playerPaddle.y, game.playerPaddle.width, game.playerPaddle.height); // コンピューターパドルを描画 ctx.fillRect(game.computerPaddle.x, game.computerPaddle.y, game.computerPaddle.width, game.computerPaddle.height); // ボールを描画 ctx.beginPath(); ctx.arc(game.ball.x, game.ball.y, game.ball.radius, 0, Math.PI * 2); ctx.fillStyle = '#fff'; ctx.fill(); // ゲームが停止中の場合メッセージを表示 if (!gameRunning) { ctx.fillStyle = '#fff'; ctx.font = '20px Courier New'; ctx.textAlign = 'center'; ctx.fillText('ゲーム開始ボタンを押してください', canvas.width / 2, canvas.height / 2 + 60); } } // ゲームのメインループ function gameLoop() { if (!gameRunning) return; updatePaddles(); updateBall(); draw(); animationId = requestAnimationFrame(gameLoop); } // ゲーム開始 function startGame() { gameRunning = true; gameLoop(); } // ゲームリセット function resetGame() { gameRunning = false; if (animationId) { cancelAnimationFrame(animationId); } // スコアリセット game.score.player = 0; game.score.computer = 0; updateScore(); // ボールパドル位置リセット game.ball.x = canvas.width / 2; game.ball.y = canvas.height / 2; game.ball.speedX = 4; game.ball.speedY = 3; game.playerPaddle.y = canvas.height / 2 - 50; game.computerPaddle.y = canvas.height / 2 - 50; draw(); } // 初期描画 draw(); </script> ``` </body> </html>

 

 

https://anond.hatelabo.jp/20250706011306#

2025-07-05

生成AIを利用したプログラミング初級者向けの温故知新提案

はじめに

ここで言う「プログラミング初級者」とはプログラミング記述が上から下へ向かって順番に処理されること、条件分岐ループという概念があることを理解しており、RPGゲームが作れる「RPGツクール(現RPG Maker)」や学童向けプログラミング環境Scratch」、「ナビつき! つくってわかる はじめてゲームプログラミング(ナビつく)」、ADVゲームが作れる「吉里吉里(もしくは吉里吉里2)」、過去BASICやC、HSPJavascriptあたりでプログラミングへ挑戦し挫折したなどなど、ある程度の「プログラマブルロジック」構築の経験がある者を指します。

前日談(初級者は読まなくて良いです)

ある時、筆者はふと思いました。「生成AIはなんだかんだで膨大なテキスト情報を処理している事がキモだよなぁ」とありきたりなことを。

そして、同時にプログラミング初級者の弱点として「現在記述されているコード管理においてテキストと実際の処理フロー脳内で一致しない」「プログラミング言語ごとに定められているルール関数予約語の把握が困難」なのが問題とも考えました。

前述したプログラミング初級者の弱点の考え自体車輪の再発明であり、「Scratch」や、より高度な「UML」が既に存在しており、特筆すべきことは何もありません。

しかし、「Scratch」や「UML」、なんなら「RPGツクール」や「吉里吉里」などに無い点として、現代では自然言語処理が大幅に向上した生成AI実用の域にまで到達しつつあるのが従来とは異なる点でした。

まり自然言語を混ぜ込みやすテキストベース言語、かつ、処理を記述するとフロー視覚的に理解やす言語可能であれば情報量が多くて一部の界隈で広く使われている言語があればプログラミング初級者も気軽にプログラミングできるのではないか?と発想しました。

そこで前述の条件を満たす1つの言語へ目を付けました。

本題

コンピュータ(コンパイラインタプリタなどソフトウェアを含む)が解することができる言語にはプログラミング言語以外にも様々あり、今回取り上げるのは「データ記述言語」と呼ばれるものです。

データ記述言語の中でもグラフ作成へ特化しており、特にフローチャート作成で真価を発揮する「DOT言語というものがあります

早速ですが、実際に手を動かしてみましょう。ちなみにDOT言語Graphviz OnlineというWebツールがあるため別途に何かしらをインストールして環境構築する必要はありません。便利な世の中ですね。

上記Graphviz Onlineを開くと、既に左側のDOT言語記述された内容が、右側で作図されています。DOT言語はこのような図を作図するためのデータ記述言語です。

一旦、左側の記述をCtrl+Aで全選択をしDeleteなどで全削除し、下記の内容をコピペしてみましょう。

digraph graphname {

A -> B;

}

一瞬で○に囲まれたAとBが繋がった図が作成されました。

DOT言語の詳細な使い方は様々なWebサイトやブログ記事Qiitaなどへ譲るとして、A - > Bの見た目から発想の転換をしてみると処理Aから処理Bという流れに見えませんか?

DOT言語は生成AIを利用する上で有利なテキストベースでありながらグラフ作成できるのがキモであり、例えばこのA -> BがA「Webページを開いたら」 → B「Hello, Worldと表示する」という風にできるのであれば処理のフロー可視化されており本当に素晴らしいことです。

Hello, worldを表示してみる

ここでプログラミング有識者は「DOT言語UMLなどに見立てて処理を記述するのは良いが、プログラミング初心者は求めた結果を出力するロジックアルゴリズムを発想する知見や経験値が圧倒的に足りていないのが問題ではないか?」と至極真っ当かつ反論余地がない問題点の指摘をすると思いますが、そこで活きるのが生成AIです。

生成AIは初級者プログラマ個人ロジックアルゴリズム作成能力無関係ロジックアルゴリズムを生成できる(可能性が少なくともある)ので、処理のフローをDOT言語記述することにより処理フローから初級者プログラマが求めるプログラムを生成AIへ推論させ生成させるというのが狙いです。実際に試してみましょう。

digraph graphname {

A[
label = "Webページを表示する"
];

B[
label = "Webページを表示したらHello, worldとテキストを表示する"
];

A -> B;

}

プログラミング有識者はこの時点で「なるほど、自然言語記述をそのままDOT言語に入れ込んでしまうのか」と気付いたのではないかと思います

このDOT言語Google Gemini 2.5 Flash「下記のデータ記述言語のDOT言語記述された処理フローJavascript言語へ変換をしWebページ上へ「Hello,world」を表示したいので変換をお願いします。」命令すると以下のコードが生成されました。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello World Page</title>
</head>
<body>
    <div id="output"></div>

    <script>
        // Webページが表示されたら(DOMが完全に読み込まれたら)実行される
        document.addEventListener('DOMContentLoaded', function() {
            // "Webページを表示する" ステップは、このHTMLファイル自体が表示されることで達成されます

            // "Webページを表示したらHello, worldとテキストを表示する" ステップ
            const outputDiv = document.getElementById('output');
            outputDiv.textContent = 'Hello, world';
        });
    </script>

</body>
</html>

フロー記述する利点は、ロジックアルゴリズムを発想する知見や経験値が足りなくとも、フローステップが明確に分かれているので生成AIが処理を切り分けて推論してくれることであり、そしてプログラミング初心者自身フローチャートを視覚確認できるので「Aを処理したらBを処理する」と切り分けて考えやすいことです。

また、求めている結果ではなく誤った結果が生成されても、A - > B - > Cとフローを細分化していくことで生成AIの推論精度を高めていくことができるのも利点です。

応用編

より生成AIへ精度の高い推論をしてもらうために補足情報を付加するのも有用です。

digraph graphname {

A[
label = "Webページを表示する"
];

B[
label = "Webページを表示したらHello, worldとテキストを表示する",
comment = "Webページが完全に読み込まれるまで待機"
];

A -> B;

}

labelの記述内容もcommentの記述内容も生成AIが推論のための情報として利用するので誤った結果が生成されてもA - > B - > Cとフローを細分化しなくとも良い場合があります

DOT言語を知るプログラミング有識者が「DOT言語仕様を考えれば確かにそうだが、その発想はなかった」と言っていただけるであろうDOT言語コード例だとこういう記述方法もアリです。

digraph 増田コード {

最初の処理[
label = "Webページを表示する"
];

次の処理[
label = "Webページを表示したらHello, worldとテキストを表示する",
comment = "Webページが完全に読み込まれるまで待機"
];

最初の処理 -> 次の処理;

}

ノード名称自然言語採用することにより、例えばゲームプログラミング時に「キャラクタージャンプする」という読んだそのままな処理のためのノード、というか一般的に言うオブジェクト作成することが可能で、後は->で繋げて処理をさせられます

ちなみに別のノード作成する際に「"キャラクタージャンプする"から継承する」の様なことをcommentなどへ記述しておくと生成AIが推論して継承します。なんならcommentなどへ「キャラクター画像image.gif使用」などと記述しておくとファイルの読み込みもします。

更にDOT言語にはカスタム要素という仕様存在しており、DOT言語仕様で定められた予約語以外も使用可能です。

digraph 増田コード {

最初の処理[
label = "Webページを表示する"
];

次の処理[
label = "Webページを表示したらHello, worldとテキストを表示する",
comment = "Webページが完全に読み込まれるまで待機",
font_style = "フォントを太字のボールド体、色を赤(#FF0000)とする"
];

最初の処理 -> 次の処理;

}

生成AIカスタム要素の名称からも推論を発揮し、上記場合であればフォントスタイル指定していると推論をするので生成AIの推論精度を高める補足情報として機能します。

まりこれはカスタム要素の名称として"Action"などの名称採用すると"動作"として推論をし、"decision"ならば"条件分岐"ですし、"input"ならば"入力"ですし、"loop"ならば"繰り返し"ですし、"Type"ならば"種別"です。

より詳細に process[type="Action"] などのノード作成してどんどん生成AIの推論精度を高めていくことが可能であり、そろそろ察してきているかと思いますが 処理[種別="動作"] と自然言語記述しても機能します。

プログラミング有識者は更に「プログラム言語自体予約語、例えばJavascriptを生成する事を前提にlengthを名称にすると配列を使おうとするのか?」と疑問に感じるでしょうがお察しの通りで生成AI配列を使おうとするので、敢えて使いたいプログラム言語機能や外部ライブラリなどがある場合は補足情報として機能する形で記述しておくと生成AIは推論へ利用します(まぁそこまで知識ある方なら該当のプログラム言語使ったほうが手っ取り早いと思いますが)。

おわりに

以上をもって「生成AIを利用したプログラミング初級者向けの温故知新提案」を終えたいと思います

色々とツッコミどころには筆者自身が気付いていて。例えば「結局はDOT言語仕様を覚えないといけないのでは?」とか「プログラミング初級者に任せると生成前のソースであるDOT言語コードスパゲッティになりそうだよな」とか「面倒くせぇから普通にプログラミング覚えろや」とか理解してますし至極真っ当かつ反論余地がないと思ってます

今回の提案プログラミング有識者向けの本質は「生成AIへ向いた中間言語の発掘」であり、「DOT言語ならそこそこ普及してるしプログラミング初級者でも扱えるんじゃね?」と業務中に発想したものを書き留め公開いたしました。

何かプログラミング有識者の皆さんからより良い発想があれば参考にしたいと考えていますのでよろしくお願いいたします。以上。

2025-07-01

[] 増田CSSを紹介する記事(英文スパム対策付き)

Chromeブラウザには増田を快適に閲覧するための コンパクトな増田 という古い拡張機能があったが、Chrome更新対応し切れておらず、既にChromeには新規インストールできなくなってしまっている。Edgeにはまだインストール可能だが、いずれ対応しなくなる可能性が高い。

そこで、「増田トップページで、言及エントリ(返信・トラバ)を一覧から除外することで、新規エントリだけを一覧できる」という機能に絞ってコンパクト増田再現、ついでにいくつかのおまけ機能付与したスタイルシート(CSS)を今年の4月に公開していたのだが、今回改めて英文スパム対策を追加したので公開する。

これを導入するには Stylus という拡張必要で、少し気軽さには欠けるが、増田以外にも活用できるので、この機会にぜひ導入してみてほしい。拡張インストールしたあとは、下記のコードコピペして新規スタイルとして導入する方法もあるが、スタイルシートを公開できる userstyles.world の増田CSSページ(※毎朝9:00直後はアクセスできない) から [Install] ボタンインストールするほうが、自動更新にも対応するので便利かもしれない。

増田CSS (7/20: 増田文字数制限のため、スパム対策部分は省略しました)

/* トップページ言及エントリを除外 */
/* via: 最近ファーストブクマカが静か https://anond.hatelabo.jp/20250326171302 */
h1/*はてな匿名ダイアリー*/ + #intro/*名前を隠して楽しく日記。*/ + #body div.section:has(h3 > a/*■*/ + a:not(.keyword, .edit)/*anond:YYYYMMDDhhmmss*/){
  display: none;
}

/* うっかりクリックしがちなキーワードリンク無効に */
a.keyword{
  pointer-events: none;
}

/* 執筆時のテキストエリアを広く */
textarea#text-body{
  min-height: 50vh !important;
}

/* 執筆時に特殊記号のヒント(疑似要素なので選択してコピペできないのがもどかしいけど) */
p.post-submit > span.explain::after{
  margin-left: 1em;
  padding-left: 1em;
  content: "特殊記号: &[&#38;] <[&#60;] >[&#62;]";
  background: url(/images/common/outsite-wh.gif) 0 3px no-repeat;
}

/* スパム対策部分は下記URLの [Install] ボタンで事前確認できます(随時更新中) */
/* https://userstyles.world/style/23028/ */

なお、このCSS適用すると、NGワードを含むこの増田自体も、増田トップページからは消えてしまう(この増田単体の個別ページなら閲覧できる)。

PCスマホ向けの導入方法

念のため、PCスマホCSS適用する方法解説にもリンクしておく。

PC: 【StylusウェブサイトCSS適用できる拡張機能自由カスタマイズ! | ナポリタン寿司PC日記

https://www.naporitansushi.com/stylus/

StylusFirefox版もある https://addons.mozilla.org/ja/firefox/addon/styl-us/

iPhone: MaKeoverアプリiPhone SafariCSSカスタマイズ万博パビリオン予約結果一覧を見やすくする使い方

https://gintachan.com/makeover-app-css-change-safari-how-to/

Android: スマートフォン AndroidFirefoxCSSカスタマイズ Stylus の使い方・初期設定方法

https://skypenguin.net/2025/06/21/post-109209/

(7/21追記) また、スパム特に多い時は、1ページまるごとスパムということもあるので、PCなら uAutoPagerize (Chrome)weAutoPagerize (Firefox) などの拡張を使うと、自動でページが継ぎ足されて快適に読み進められる。ただし、継ぎ足ししまくるとメモリ不足などでブラウザが重くなることがあるので、そうなったら page: 20 などのページ番号をクリックしてから続きを読もう。

(参考) 増田の頻出キーワードリンク上位20抽出JavaScript

また、スパム対策の簡易NGワードは、下記のスクリプトを使って抽出した「直近の増田の頻出キーワードリンク上位20件」から誤判定しそうな lineuser を除いた18件を用いた。10件だと生き残る英文スパムがあったので20件にしたが、それでもわずかに洩れはある。しか日本語による真っ当な(?)増田の直近の誤判定はなかった。はてなキーワードリンクだけを対象にしているので、URLにはこれらのキーワードが入っていても大丈夫だ。ただし、スパムトレンドが変われば話は変わってくるかもしれないし、過去未来増田誤判定は当然あるだろう。気になる人は前掲のCSSを行単位編集してほしい。

// AutoPagerizeでまとまった数のページを読み込ませた後に実行するとよい。
(function(){
  const keywords = [];
  // はてなキーワードの集計
  document.querySelectorAll('a.keyword').forEach(a => {
    // 4文字未満は誤検出の可能性が高まるので除外
    if(a.textContent.length < 4) return;
    let index = keywords.findIndex(k => k.keyword === a.textContent);
    if(index >= 0) keywords[index].count += 1;
    else keywords.push({keyword: a.textContent, count: 1});
  });
  keywords.sort((a, b) => a.count < b.count);
  // ランキング配列の出力
  console.log(keywords);
  // CSS埋め込み用に上位キーワードのみをURIエンコードして出力
  console.log(keywords.slice(0, 20).map(k => encodeURIComponent(k.keyword)).join('\n'));
})();

謝辞

anond:20250326171302 ←元はこの増田きっかけでした。

anond:20250701194328キーワード判定に踏み切る後押しとなりました。

2025-06-13

我が名はサイボーグdorawii

パーマリンク署名対象にするより堅牢自動化を作れた。

一度投稿したうえで別タブを開いてプログラム的(fetch)に送信してその別タブが閉じられる仕組み。

改めてスクリプト配布しちゃる

最初投稿してエントリページに移動した親タブ側のjsコード
// ==UserScript==
      // @name         PGP署名検出と別タブ自動編集
      // @namespace    http://tampermonkey.net/
      // @version      1.0
      // @description  PGP署名がない投稿自動編集ページへ誘導
      // @match        https://anond.hatelabo.jp/*
      // @grant        GM_setValue
      // @grant        GM_getValue
      // @grant        GM.openInTab
      // ==/UserScript==

      (function () {
        'use strict';

        const body = document.getElementById('entry-page');
        if (!body) return;

        const titleText = document.title;
        if (!titleText.includes('dorawii')) return;

        const pgpRegex = /BEGIN.*PGP(?: SIGNED MESSAGE| SIGNATURE)?/;
        const preElements = document.querySelectorAll('div.body pre');
        let hasPgpSignature = false;

        for (const pre of preElements) {
          if (pgpRegex.test(pre.textContent)) {
            hasPgpSignature = true;
            break;
          }
        }

        if (hasPgpSignature) return;

        const editLink = document.querySelector('a.edit');
        const childTab = GM.openInTab(editLink.href, { active: false, insert: true, setParent: true });

      })();
親タブから開かれる編集ページの子タブのjsコード
 // ==UserScript==
      // @name         編集ページ処理と自動送信・閉じ
      // @namespace    http://tampermonkey.net/
      // @version      1.0
      // @description  編集ページで署名処理と送信、タブ自動閉じ
      // @match        https://anond.hatelabo.jp/dorawii_31/edit?id=*
      // @grant        GM_getValue
      // @grant        GM_xmlhttpRequest
      // @grant        GM_setClipboard
      // @grant        GM_notification
      // @connect      localhost
      // ==/UserScript==

      (async function () {
        'use strict';

        const shouldRun = await GM_getValue('open-tab-for-edit', '0');

        const textareaId = 'text-body';
        const textarea = document.getElementById(textareaId);

        if (!textarea) return;

        const content = textarea.value;

        const pgpSignatureRegex = /-----BEGIN PGP SIGNED MESSAGE-----[\s\S]+?-----BEGIN PGP SIGNATURE-----[\s\S]+?-----END PGP SIGNATURE-----/;
        if (pgpSignatureRegex.test(content)) {
          console.log('[PGPスクリプト] 署名が検出されたためそのまま送信します');
          return;
        }

        const httpRequest = (url, data) =&gt; {
          return new Promise((resolve, reject) =&gt; {
            GM_xmlhttpRequest({
              method: 'POST',
              url: url,
              headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
              data: `value=${encodeURIComponent(data)}`,
              onload: function (response) {
                resolve(response.responseText);
              },
              onerror: function (error) {
                reject(error);
              }
            });
          });
        };


        // textarea の値を取得
        // 1. 現在のページのURLからURLオブジェクト作成
        const currentUrl = new URL(window.location.href);

        // 2. ベースとなる部分 (例: "https://anond.hatelabo.jp") を取得
        const origin = currentUrl.origin;

        // 3. 'id' パラメータの値 (例: "20250610184705") を取得
        const idValue = currentUrl.searchParams.get('id');

        // 4. ベース部分とIDを結合して、目的URL文字列を生成
        //    idValueが取得できた場合のみ実行する
        let newUrl = null;
        if (idValue) {
          newUrl = `${origin}/${idValue}`;
        }

        // 5. 生成されたURL変数に代入し、コンソールに出力して確認
        console.log(newUrl);
        const valueToSend = newUrl;

        try {
          const signatureText = await httpRequest('http://localhost:12345/run-batch', valueToSend);
          console.log('バッチ応答:', signatureText);
          if (!signatureText.includes('BEGIN PGP SIGNED MESSAGE')) {
            alert('PGP署名クリップボードに見つかりませんでした。');
            return;
          }

          const newText = content.replace(/\s*$/, '') + '\n' + signatureText + '\n';
          textarea.value = newText;

          console.log('[PGPスクリプト] 署名を貼り付けました。送信を再開します。');


          const form = document.forms.edit;

          const newForm = form.cloneNode(true);
          form.replaceWith(newForm);

          newForm.addEventListener('submit', async (e) =&gt; {
            e.preventDefault(); // HTML標準のsubmitをキャンセル
            const bodyText = textarea?.value || '';

            // reCAPTCHA トークンの取得
            const recaptchaToken = await new Promise((resolve) =&gt; {
              grecaptcha.enterprise.ready(() =&gt; {
                grecaptcha.enterprise.execute('hoge', { action: 'EDIT' })
                  .then(resolve);
              });
            });

            // POSTするデータの構築
            const formData = new FormData(newForm);
            formData.set('body', bodyText);
            formData.set('recaptcha_token', recaptchaToken);
            formData.set('edit', '1');
            try {
              const response = await fetch(newForm.action, {
                method: 'POST',
                body: formData,
                credentials: 'same-origin'
              });


              if (response.ok) {
                console.log('送信成功');
                window.close();


              } else {
                console.error('送信失敗', response.status);
              }
            } catch (err) {
              console.error('送信中にエラーが発生', err);
            }

          });

          // プログラム的に送信トリガー
          newForm.dispatchEvent(new Event('submit', { bubbles: true }));

        } catch (e) {
          console.error('バッチ呼び出し失敗:', e);
        }

      })();
node.jsで動かすローカルサーバーコード
const http = require('http');
const { exec } = require('child_process');
const querystring = require('querystring');

const server = http.createServer((req, res) =&gt; {
  if (req.method === 'GET' &amp;&amp; req.url === '/ping') {
    res.writeHead(200);
    res.end('pong');
  } else if (req.method === 'POST' &amp;&amp; req.url === '/run-batch') {
    let body = '';

    req.on('data', chunk =&gt; {
      body += chunk.toString();
    });

    req.on('end', () =&gt; {
      const parsed = querystring.parse(body);
      const value = parsed.value || 'default';

      // 値を引数としてバッチに渡す
      exec(`C:\\Users\\hoge\\Desktop\\makesign.bat "${value}"`, { encoding: 'utf8' }, (err, stdout, stderr) =&gt; {
        if (err) {
          res.writeHead(500);
          res.end('Error executing batch: ' + stderr);
        } else {
          res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
          res.end(stdout.trim());
        }
      });
    });

  } else {
    res.writeHead(404);
    res.end('Not found');
  }
});

server.listen(12345, () =&gt; {
  console.log('Batch server running at http://localhost:12345/');
});
@echo off
setlocal enabledelayedexpansion


:: 署名するファイルset "infile=%~1"
set outfile=%TEMP%\pgp_output.asc

:: 以前の出力があれば削除
if exist "%outfile%" del "%outfile%"


:signloop
:: AutoHotkeyパスフレーズ入力(gpgがパスワード要求するダイアログが出た場合に備える)
start "" /b "C:\Users\hoge\Documents\AutoHotkey\autopass.ahk"

:: PGPクリア署名作成
echo %infile% | gpg --yes --clearsign --output "%outfile%"


:: 署名成功していればループを抜ける
if exist "%outfile%" (

    goto postprocess
) else (

    timeout /t 1 &gt; nul
    goto signloop
)
:postprocess

powershell -nologo -command ^
  "$header = '&gt;|'; $footer = '|&lt;'; $body = Get-Content '%outfile%' -Raw; Write-Output ($header + \"`r`n\" + $body + $footer)"

powershell -nologo -command ^
  "$header = '&gt;|'; $footer = '|&lt;'; $body = Get-Content 'signed.asc' -Raw; Set-Clipboard -Value ($header + \"`r`n\" + $body + $footer)"

endlocal
exit /b
AutoHotkey(以前と同じ)
#Persistent
#SingleInstance ignore
SetTitleMatchMode, 2
WinWaitActive, pinentry
SendInput password
Sleep 100
SendInput {Enter}
ExitApp

動けばいいという考えで作っているので余分なコードも含んでいるかもしれない。

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

https://anond.hatelabo.jp/20250613185036 
-----BEGIN PGP SIGNATURE-----

iHUEARYKAB0WIQTEe8eLwpVRSViDKR5wMdsubs4+SAUCaEv1FQAKCRBwMdsubs4+
SHHkAQDUOLgBcdji2T6MJ7h/vlMdFfGlWAzNdXijjE1gIuEPywEAiMNMZqhrMmtl
c7UqRuggNJ/UTa5xTIcKp622+7jJQQg=
=Lgkl
-----END PGP SIGNATURE-----

2025-06-09

dorawii

ようやく(ほぼ)すべてが自動化された。

あとはローカルサーバーの起動をスタートアップに設定する(方法AIに聞いて指示に従う)だけの消化試合

ここにほとんどAI頼りのコードを公開しておく。

事前にインストールしておくもの

autohotkey

nodejs

ユーザースクリプトを実行できる拡張機能

パスとかの注意

署名要求してくるパスワードを自動入力するahkファイルドキュメントAutoHotkey配下に置いた。

バッチファイル(make.sign.bat)はデスクトップに置いた。

以下コード

autopass.ahk
#Persistent
#SingleInstance ignore
SetTitleMatchMode, 2
WinWaitActive, pinentry
SendInput お前のパスワード
Sleep 100
SendInput {Enter}
ExitApp
run-bacth-server.js
// run-batch-server.js
const http = require('http');
const { exec } = require('child_process');

const server = http.createServer((req, res) =&gt; {
  if (req.url === '/ping') {
    res.writeHead(200);
    res.end('pong');
  } else if (req.url === '/run-batch') {
    exec('C:\\Users\\you\\Desktop\\makesign.bat', (err) =&gt; {
      res.writeHead(200);
      res.end(err ? 'Error' : 'OK');
    })
    ;
  } else {
    res.writeHead(404);
    res.end('Not found');
  }
});

server.listen(12345, () =&gt; {
  console.log('Batch server running at http://localhost:12345/');
});
makesign.bat
@echo off
setlocal enabledelayedexpansion

:: ミリ秒単位UTC時刻を取得
for /f %%a in ('powershell -nologo -command "[int64]::Parse((Get-Date).ToUniversalTime().ToString('yyyyMMddHHmmssfff'))"') do set timestamp=%%a

:: 署名するファイルset infile=%TEMP%\pgp_input.txt
set outfile=%TEMP%\pgp_output.asc

:: 以前の出力があれば削除
if exist "%outfile%" del "%outfile%"

:: タイムスタンプを原文として保存
echo %timestamp% &gt; "%infile%"

:signloop
:: AutoHotkeyパスフレーズ入力(gpgがパスワード要求するダイアログが出た場合に備える)
start "" /b "C:\Users\infini\Documents\AutoHotkey\autopass.ahk"

:: PGPクリア署名作成
gpg --yes --clearsign --output "%outfile%" "%infile%"


:: 署名成功していればループを抜ける
if exist "%outfile%" (
    echo [INFO] 署名成功
    goto postprocess
) else (
    echo [WARN] 署名失敗、再試行します…
    timeout /t 1 &gt; nul
    goto signloop
)
:postprocess

:: PowerShellで余計な改行なしに |&lt; をつけてクリップボードコピー
powershell -nologo -command ^
  "$header = '&gt;|'; $footer = '|&lt;'; $body = Get-Content '%outfile%' -Raw; Set-Clipboard -Value ($header + \"`r`n\" + $body + $footer)"

echo Done. signed.asc created and clipboard updated (no extra blank line).
endlocal
exit /b
tempermonkeyとかに登録するユーザースクリプト
// ==UserScript==
// @name         PGP署名自動付加スクリプト(GM_xmlhttpRequest版)
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  投稿前にPGP署名を付けてから送信(fetch未使用)
// @match        https://anond.hatelabo.jp/dorawii_31/edit*
// @grant        GM_xmlhttpRequest
// @grant        GM_setClipboard
// @grant        GM_notification
// / @connect      localhost
// ==/UserScript==

(function () {
  'use strict';

  const submitId = 'submit-button';
  const textareaId = 'text-body';
  const localServer = 'http://localhost:12345/run-batch';

  const pgpSignatureRegex = /-----BEGIN PGP SIGNED MESSAGE-----[\s\S]+?-----BEGIN PGP SIGNATURE-----[\s\S]+?-----END PGP SIGNATURE-----/;

  const httpRequest = (url) =&gt; {
    return new Promise((resolve, reject) =&gt; {
      GM_xmlhttpRequest({
        method: 'GET',
        url: url,
        onload: function (response) {
          resolve(response.responseText);
        },
        onerror: function (error) {
          reject(error);
        }
      });
    });
  };

  const interceptClick = () =&gt; {
    const btn = document.getElementById(submitId);
    if (!btn || btn.dataset.pgpIntercepted === 'true') return;
    btn.dataset.pgpIntercepted = 'true';

    btn.addEventListener('click', async function (e) {
      const textarea = document.getElementById(textareaId);
      if (!textarea) return;

      const content = textarea.value;

      if (pgpSignatureRegex.test(content)) {
        console.log('[PGPスクリプト] 署名が検出されたためそのまま送信します');
        return;
      }

      e.preventDefault();
      e.stopImmediatePropagation();
      console.log('[PGPスクリプト] 署名が見つからないため処理を停止し、署名を取得します');

      try {
        await httpRequest(localServer); // バッチ実行

        const signatureText = await navigator.clipboard.readText();
        if (!signatureText.includes('BEGIN PGP SIGNED MESSAGE')) {
          alert('PGP署名クリップボードに見つかりませんでした。');
          return;
        }

        const newText = content.replace(/\s*$/, '') + '\n' + signatureText + '\n';
        textarea.value = newText;

        console.log('[PGPスクリプト] 署名を貼り付けました。送信を再開します。');
        btn.click(); // イベント再発火

      } catch (err) {
        alert('PGP署名の取得または貼り付けに失敗しました。\n' + err);
      }
    }, true);
  };

  window.addEventListener('load', () =&gt; {
    setTimeout(interceptClick, 1000);
  });
})();

プロミスメソッドとか全然まだ理解してなくてそのなかに関数代入したその関数オブジェクトプロパティresponseを?いやまあそのあたりのコードが示すデータの流れが全然理解できないような人間でもここまでできちゃった。

AIすごいなと思うよ。そして思うのは今後重要になってくるのは文法とか自体に詳しいことじゃなくて、そのプログラムの処理内容を指示できるシステムエンジニア的な言語化能力のほうじゃないかなと思った。

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

20250609111559680 
-----BEGIN PGP SIGNATURE-----

iHUEARYKAB0WIQTEe8eLwpVRSViDKR5wMdsubs4+SAUCaEbCbwAKCRBwMdsubs4+
SLueAPwOv7PBk4voAe5qlcCEvs/PJhmKc5QAb/1R43JMQFuDZgD/UTPEKsL/PhK9
jFGv2HDXK1dVjLNwvosgX9uYJh5xxwY=
=qiOE
-----END PGP SIGNATURE-----

2025-04-07

anond:20250407021332

ちなみに

JSで0インデックスがヘダーのストリングアレイアレイからなるテーブルカラムを別アレイカラムインフォオブジェクトディスプレイナンバーに合わせて並び替えて名前nameからdisplayNameの値に入れ替えるコード

だと

想定される入力

const table = [

['id', 'name', 'age'], // ヘッダー

['1', 'Alice', '30'],

['2', 'Bob', '25'],

];

const columnInfo = [

{ name: 'name', displayName: '名前', displayNumber: 0 },

{ name: 'age', displayName: '年齢', displayNumber: 1 },

{ name: 'id', displayName: 'ID', displayNumber: 2 }

];

出力例(上記データ場合

[

['名前', '年齢', 'ID'],

['Alice', '30', '1'],

['Bob', '25', '2']

]

function reorderAndRenameTable(table, columnInfo) {

const header = table[0];

const body = table.slice(1);

// nameindexマッピングを取得

const nameToIndex = header.reduce((acc, name, idx) =&gt; {

acc[name] = idx;

return acc;

}, {});

// displayNumber順にカラム情報ソート

const sortedColumns = [...columnInfo].sort((a, b) =&gt; a.displayNumber - b.displayNumber);

// 新しいヘッダーを displayName に置き換え

const newHeader = sortedColumns.map(col =&gt; col.displayName);

// 各行の値も同じ順で並び替える

const newBody = body.map(row =&gt; {

return sortedColumns.map(col =&gt; row[nameToIndex[col.name]]);

});

return [newHeader, ...newBody];

}

ここまで数秒なのでまあこれくらいだと書くよりは早い

2025-03-22

anond:20250322220222

id を手動でコピーしてくるのが面倒だったから、削除ボタンが画面に出るようにした

押すと即削除

const rkm = "(トークン的なもの)"
const user = "(ユーザ名)"

const delmasda = (id) =&gt;
    fetch(`https://anond.hatelabo.jp/${user}/edit`, {
        "headers": {
            "content-type": "application/x-www-form-urlencoded",
        },
        "referrer": "https://anond.hatelabo.jp/",
        "referrerPolicy": "origin",
        "body": new URLSearchParams({
            "rkm": rkm,
            "mode": "confirm",
            "id": id,
            "delete": "削除する"
        }).toString(),
        "method": "POST",
    })

for (const sec of document.querySelectorAll(".section")) {
    const id = sec.querySelector("a").href.match(/92;/(92;d{14})/)[1]
    const delbtn = document.createElement("button")
    delbtn.textContent = "削除"
    delbtn.onclick = async () =&gt; {
        await delmasda(id)
        sec.remove()
    }
    sec.querySelector(".edit").after(delbtn)
}

増田の削除をひとつひとつ詳細画面開いてするのが面倒だからまとめて消すスクリプトやで

 

const rkm = "(トークン的なもの)"
const ids = ["(削除する増田ID)"]
const user = "(ユーザー名)"

for (const id of ids)
    fetch(`https://anond.hatelabo.jp/${user}/edit`, {
        "headers": {
            "content-type": "application/x-www-form-urlencoded",
        },
        "referrer": "https://anond.hatelabo.jp/",
        "referrerPolicy": "origin",
        "body": new URLSearchParams({
            "rkm": rkm,
            "mode": "confirm",
            "id": id,
            "delete": "削除する"
        }).toString(),
        "method": "POST",
    })

増田3分以上投稿されない時間

増田で 3 分以上投稿されない期間があるのか気になったから調べた

直近の 1 日だとこれだけあった

 

2025-03-22 00:14 -- 2025-03-22 00:18
2025-03-22 00:10 -- 2025-03-22 00:14
2025-03-21 07:56 -- 2025-03-21 08:00
2025-03-21 07:50 -- 2025-03-21 07:56
2025-03-21 07:44 -- 2025-03-21 07:48
2025-03-21 07:28 -- 2025-03-21 07:32
2025-03-21 06:58 -- 2025-03-21 07:03
2025-03-21 06:45 -- 2025-03-21 06:54
2025-03-21 06:32 -- 2025-03-21 06:37
2025-03-21 05:56 -- 2025-03-21 06:04
2025-03-21 05:51 -- 2025-03-21 05:56
2025-03-21 05:34 -- 2025-03-21 05:38
2025-03-21 05:30 -- 2025-03-21 05:34
2025-03-21 05:00 -- 2025-03-21 05:09
2025-03-21 04:56 -- 2025-03-21 05:00
2025-03-21 04:45 -- 2025-03-21 04:50
2025-03-21 04:09 -- 2025-03-21 04:13
2025-03-21 03:41 -- 2025-03-21 03:45
2025-03-21 03:29 -- 2025-03-21 03:39
2025-03-21 03:03 -- 2025-03-21 03:07
2025-03-21 02:56 -- 2025-03-21 03:02
2025-03-21 02:44 -- 2025-03-21 02:48
2025-03-21 02:33 -- 2025-03-21 02:37
2025-03-21 02:21 -- 2025-03-21 02:27
2025-03-21 02:14 -- 2025-03-21 02:19

 

秒はみてないから 00:01:01 - 00:03:59 はほぼ 3 分だけど 2 分扱いだし、 00:01:59 - 00:04:00 はほぼ 2 分だけど 3 分扱いになるくらいの誤差はある

 

日によって違うだろうし、曜日の影響も大きそうだから 1 ヶ月分くらい調査しようかと思ったけど、

増田の量が思いの外多すぎて 1 日分だけでも 100 ページ以上取得しないといけなかった

件数だと 2500 以上

 

その量の収集は大変だし規制掛かりそうだから諦めた

一応取得に使ったコードも載せとく

そんなきれいなコードでもないけど

 

import { setTimeout } from "node:timers/promises"
import { Browser } from "happy-dom"

const getTimestamps = async function* () {
	const browser = new Browser()
	const page = browser.newPage()

	try {
		for (let num = 1; ; num++) {
			await setTimeout(3000)
			await page.goto(`https://anond.hatelabo.jp/?page=${num}`)

			const days = page.mainFrame.document.querySelectorAll(".day")
			for (const day of days) {
				const date = day.querySelector("h2 .date").textContent.trim()
				for (const footer of day.querySelectorAll(".sectionfooter")) {
					const time = footer.textContent.match(/\d{2}:\d{2}/)[0]
					yield `${date} ${time}`
				}
			}
		}
	} finally {
		await page.close()
		await browser.close()
	}
}

const diff = (a, b) =&gt; {
	return new Date(b + ":00") - new Date(a + ":00")
}

let prev = null
for await (const datetime of getTimestamps()) {
	if (prev &amp;amp;&amp;amp; diff(datetime, prev) &gt; 1000 * 60  * 3) {
		console.log(datetime, prev)
	}
	prev = datetime
}

 

結果をみると昼間はずっと深夜から早朝にかけてときどきある

基本は空いても 5 分程度であり、最大でも 10 分となっている

投稿が少ないと感じるときもあるが、賑わってる方だといえる

2025-03-10

LLMで50個くらいのjavascriptのvarをconst,letに置き換えさせたら

1Mトークン以上かかったわ

2025-03-06

今のフロントエンドコードを書いてるとCOBOLの頃を思い出す

constを上から順にひたすら羅列していく感じがなんかね

と思うオブジェクト指向おじさんなのだった

以下chatgptによるremixjsの例

import { useEffect, useState } from "react";

import { json } from "@remix-run/node";

import { useLoaderData } from "@remix-run/react";

export const loader = async () =&gt; {

const response = await fetch("https://api.example.com/data");

const data = await response.json();

return json({ initialData: data });

};

export default function Index() {

const { initialData } = useLoaderData();

const [data, setData] = useState(initialData);

useEffect(() =&gt; {

const fetchData = async () =&gt; {

const response = await fetch("http://api.com/get");

const newData = await response.json();

setData(newData);

};

fetchData();

}, []);

if (initialData.value === null || data.value === null) {

return (

データの取得に失敗しました。

);

}

return (

&lt;h1&gt;Remix: SSR + クライアント処理&lt;/h1&gt;

サーバーサイドの初期データ: {initialData.value}

クライアント更新したデータ: {data.value}

);

}

ブクマエントリーページを開くブックマークレット

ちょっと長いけど、拡張機能より便利かなと。

javascript:(()=&gt;{const%20loc=document.querySelector('link[rel="canonical"]')||location;const%20urlStr=(loc.href.startsWith('about:reader'))?decodeURIComponent(loc.href.slice(17)):loc.href;const%20url=new%20URL(urlStr);let%20s=url.search;s=(s.startsWith('?page=')||s.startsWith('?P='))?"":s;const%20w=window.open('https://b.hatena.ne.jp/entry?url='+encodeURIComponent(url.origin+url.pathname+s));w.document.close();})();

2025-02-04

for文を使いたくないのはletを使いたくないか

https://gakuzzzz.github.io/slides/for_loop_to_higher_order_functions/#1

久々にアホな記事を見たので反論しておく

そもそも関数型にする大きな動機は「バグを減らすため」

これはオブジェクト指向でも同じでみんな「バグを減らすため」にいろんなパラダイムに挑戦してる

それ以外のHowだとかWhatだとかオブジェクト世界を表すだとかどうでもいい

結果的バグが減るならそれでいい

for文よりmapとかfilterの方がなぜバグが少ないか、というと「余計な操作が入りにくいから」

特にletで宣言してるような書き換え可能変数っていうのはバグの温床

例でも挙がってるようなProductのpriceの書き換えでもfor文にするとどこかにletな変数を置かないといけない

そんでletな変数っていうのはうっかり消してしまったりうっかり書き換えてしまっても気付くことができない

からconstで固めて何かしらのリスト処理をするときmapなりfilterなりを使ってconstに固め直す(か、そのまま使う)

逆に言うとそういう処理がないならfor文使っても全然構わない

本当に繰り返し処理をするんだから何も問題無い

この書き換え不可能変数を作るっていうのはCだとかC++だとかJavaだとかの頃からずーっと一緒でとにかく固めておきたい変数final宣言して書き換えさせない

そうしないと、めちゃくちゃ分かりづらいバグが混入して無意味に1週間とか過ごすことになる

Product.priceを直接書き換えるのはいいの?っていう疑問があるかもしれないが

これもしっかり規制するってのがオブジェクト指向的考え方で

「そこまでせんでも致命的にはならん」

っていう感じで型のチェックだけするのがTypeScript

まぁTSでもClass作れるから好きにすればいいんだけど

とにかくプログラミングに関する規則でHowだとかWhatだとかそういうフワフワしたこと言い出したら要注意

たぶん大学とかでフワフワしたプログラミングだけしてて碌なプロダクト作ったことない

ちゃんとしたプログラマーは

「これでバグが減ります(そして生産性が上がります)」

と言ってくれるから、そういうやつだけ重宝しろ

2024-10-08

anond:20241007235856

 

光の速さは一定

マイケルンモーレーという実験がある

地球宇宙空間を動いているのだから地球の進行方向と垂直方向では光の速さが変わるだろう。そう考えて実験してみたところ、どちらの速さも変わらなかった。つまり、どんな系でも光の速さは一定であるらしい。

 

これを式にするとこうなる。

光の速さをc, 時刻 t の間に光の進む距離を x として

x/t = c

式変形すると

(ct)^2 - x^2 = const = 0

おや、なんだか見たことある形になったね

 

空間回転を考えよう

ここで一旦休憩。座標系を回転させても'棒の長さは一定'という式を考えてみよう

x^2 + y^2 = const

かんたんのため z 方向は考えない

この時座系を回転させる式を行列で書くと

 

x' = | cos  -sin | x

y'    | sin  cos | y

こうなる。(心の目で読んで欲しい)

cos^2 + sin^2 = 1

という式を思い出すと

x'^2 + y'^2 = x^2 + y^2 = const

であることが確かめられると思う

 

戻って光の速さが一定の式

(ct)^2 - x^2 = const = 0

上の'棒の式'とは符号が逆だね。こんなときsin cos ではなく sinh cosh を使う。

 

cosθ = ((exp iθ) + (exp -iθ))/2

sinθ = ((exp iθ) - (exp -iθ))/2

 

sin cos は↑の定義だったのに対して

sinh cosh は↓の定義

 

coshθ = ((exp θ) + (exp -θ))/2

sinhθ = ((exp θ) - (exp -θ))/2

 

計算すると

cosh^2 - sinh^2 = 1 になるのがわかると思う。

cos^2 + sin^2 = 1 とは符号が逆になってるね

 

光の速さが系を変換しても変わらないという式を行列で書くと

ct' = | cosh  -sinh | ct

x'   | -sinh  cosh | x

こうなる。 これがローレンツ変換

 

(ct')^x - x'^2 = (ct)^2 - x^2

であることが確かめられると思う。

 

棒の長さが一定、つまり空間回転は空間方向 (x,y,z)しか混ぜないけれど、

光のはやさが一定、つまりローレンツ変換時間空間 (t, x ) を混ぜているでしょ?

 

時間が遅れる

速さ v で進むロケットを考えてみよう。

地上では昇くんがロケット観測している。

t 時間後に到達した距離を x として

v=x/t  

だ。

一方、ロケットには美加子さんが乗っていてその携帯電話の表示では地球を発ってから T時間である

Tを計算してみよう。

 

先程のローレンツ変換の式に代入すると

 

cT = ct cosh - x sinh = ct ( cosh - v/c sinh)

ここで x = ct を使ったよ。最後cosh で全体を纏める

= ct cosh ( 1 - tanh^2)

= ct (1/cosh)

になる。

ここまで誤魔化していたけど、cosh はロケットの速さ v で決まるパラメータ

1/cosh = \sqrt{1-(v/c)^2}

なんだ。天下り申し訳ないけど、増田では式も図も書けないので導出は勘弁して欲しい

とにかくまとめると

T = t \sqrt{1-(v/c)^2}

だね。ロケットの速度 v は光速度以下なので T < t になる。

地上で待つ昇くんが大学生になっても美加子さんが中学生のままなのはこんなワケだね

v が大きくなるほど時間の遅れは大きくなるよ




 

2024-09-22

AIインフルエンサーたちはChatGPTが世に出る前は何を投稿していたのか?(2/2)

AIインフルエンサーたちはChatGPTが世に出る前は何を投稿していたのか?(1/1)の続き

吉見拓哉|Takuya Yoshimi

このTwitterアカウントは、主にYouTubeSEO競馬アニメなどの話題について呟いており、自身活動や興味関心について発信しています

YouTubeプレミアムが500円増額。しかし、、、不可逆!!!

shimayuz@AIクリエイター

このアカウントは、日々の米国株暗号通貨市場動向、特にテクノロジー関連株やビットコイン価格変動についてツイートしています

ゼロコロナ政策再び。 $AAPL生産に影響が懸念され大きく下落。更に経済混乱が想定され、株価はSP500全業種で

一方で年末商戦売上堅調な滑り出しで $AMZN

暗号資産レンディングBlockFiが経営破綻暗号通貨も軒並み下落。

チャート上、なんとか踏み止まるか? 」

元木大介ᯅシステム生成AI Babel/Zoltraak & 生成AI塾

このアカウントは、AI特にプロマネAIや量子AIに関する話題や、仕事効率化、プログラミング、そして最新の技術トレンドについて呟いています

"プロマネAI実証実験を開始しました!量子AI×NotionによるプロマネAI実証実験を開始 https://prtimes.jp/main/html/rd/p/000000003.000082094.htmlvia @PRTIMES_JP"

sangmin.eth | Dify Ambassador

このアカウントは、主にOpenAIの最新言語モデルtext-davinci-003」の進化と、そのモデルを用いた英語学習ツールサービスについて呟いています特に英語学習におけるAI活用とその重要性を強調しています

"朝起きたら世界がまた変わっていた(笑)。@OpenAI が最新モデルtext-davinci-003」を発表。主な特徴は、①より明確で、説得力のある文章が書ける②より複雑な指示にも対応③より長い形式コンテンツが生成可 1月に"InstructGPT"が出た時も感動したけどそれを遥かに上回る進化、です。"

深津 貴之 / THE GUILD

このアカウントは、主にAI特に画像生成AIに関する話題を呟いています特にStable Diffusionのバージョンアップや使い方について多くのツイートをしています

結果

ここにリストアップした人たち以外もみましたが、分類すると3種類のアカウントがいて

1. ChatGPTブーム後にアカウントを始めた人

2. NFTなどの儲かりそうな技術を追いかけていた人

3. 日常ツイートをしていたが目覚めてしまった人

共通する特徴としては会社代表の人が多いです。

またIDを変更してログがヒットしない人は含まれていません(IDから特定できるけどそこまでやらなかった)

ツイートを取得するJS

javascript:(function() {

const text = Array.from(document.querySelectorAll('[data-testid="tweetText"]')).map(s =&gt; s.textContent.trim()).join('\n');

const textarea = document.createElement('textarea');

textarea.value = text;

document.body.appendChild(textarea);

textarea.select();

try {

document.execCommand('copy');

console.log('結果がクリップボードコピーされました!');

} catch (err) {

console.error('クリップボードへのコピーに失敗しました:', err);

}

document.body.removeChild(textarea);

})();

次の25件>
ログイン ユーザー登録
ようこそ ゲスト さん