<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>雨雪冰屋</title><description>雨雪的工作台</description><link>https://iamyukino.cn/blog/</link><language>zh_CN</language><item><title>雨雪的棉花糖完成啦！冰屋友链接力平台</title><link>https://iamyukino.cn/blog/posts/about-this-site/</link><guid isPermaLink="true">https://iamyukino.cn/blog/posts/about-this-site/</guid><description>冰屋棉花糖是一个提供友圈聚合和友链接力的平台。本文简单介绍了主站雨雪冰屋和棉花糖站的技术框架，以及从0配置冰屋的步骤。</description><pubDate>Mon, 08 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://iamyukino.cn/blog/_astro/p3.CLBZ4vre_1TCF5s.webp&quot; alt=&quot;技术栈&quot; /&gt;&lt;/p&gt;
&lt;p&gt;可爱的个人网站！主站、棉花糖、资源站和博客站前后端分离，后端均基于&lt;code&gt;Python3&lt;/code&gt; &lt;code&gt;Django5&lt;/code&gt; &lt;code&gt;mod_wsgi&lt;/code&gt;框架和&lt;code&gt;mysql8.1&lt;/code&gt;数据库，前端依赖&lt;code&gt;jQuery&lt;/code&gt;和&lt;code&gt;Swup&lt;/code&gt;。博客站前端额外采用&lt;code&gt;Astro&lt;/code&gt;框架静态生成。整体框架和后端技术栈简洁实用，易于维护。&lt;/p&gt;
&lt;h2&gt;功能&lt;/h2&gt;
&lt;p&gt;冰屋主站&lt;code&gt;/&lt;/code&gt;前端基于Bootstrap+jQuery+Popper，提供基础信息阅读、站点聚合和更新推送服务。您可修改此页的文本内容，来详细介绍您和您的项目的具体信息以及决定朋友圈文章推送、留言推送范围等。页面开发时间跨度长，技术栈可能有些过时。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://iamyukino.cn/siteshot.jpg&quot; alt=&quot;冰屋主站&quot; /&gt;&lt;/p&gt;
&lt;p&gt;棉花糖站（友链接力）前端基于jQuery+Swup，后端基于Django，包含
&amp;lt;a href=&quot;https://iamyukino.cn/ask/circle&quot; target=&quot;_blank&quot;&amp;gt;朋友圈（好文推荐）&amp;lt;/a&amp;gt;&lt;code&gt;/ask/circle&lt;/code&gt;、&amp;lt;a href=&quot;https://iamyukino.cn/ask/tp&quot; target=&quot;_blank&quot;&amp;gt;友链接力&amp;lt;/a&amp;gt;&lt;code&gt;/ask/tp&lt;/code&gt;、&amp;lt;a href=&quot;https://iamyukino.cn/ask/friends&quot; target=&quot;_blank&quot;&amp;gt;友链申请&amp;lt;/a&amp;gt;和&amp;lt;a href=&quot;https://iamyukino.cn/ask/friends&quot; target=&quot;_blank&quot;&amp;gt;状态检测&amp;lt;/a&amp;gt;&lt;code&gt;/ask/friends&lt;/code&gt;、&amp;lt;a href=&quot;https://iamyukino.cn/ask&quot; target=&quot;_blank&quot;&amp;gt;留言板&amp;lt;/a&amp;gt;&lt;code&gt;/ask&lt;/code&gt;、&amp;lt;a href=&quot;https://iamyukino.cn/ask/donate&quot; target=&quot;_blank&quot;&amp;gt;赞助支持&amp;lt;/a&amp;gt;&lt;code&gt;/ask/donate&lt;/code&gt;等多个模块。&lt;/p&gt;
&lt;p&gt;用户可填写表单申请友链，审核通过后，将在棉花糖站展示，同时加入冰屋友链接力的随机名单。加入后可在您的页面中加入我们的&amp;lt;a href=&quot;https://iamyukino.cn/ask/tp&quot; target=&quot;_blank&quot;&amp;gt;友链接力&amp;lt;/a&amp;gt;链接&lt;code&gt;https://iamyukino.cn/tp?from=这里写你的域名&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;冰屋棉花糖爬虫使用规范的请求头：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{&quot;User-Agent&quot;: &quot;Mozilla/5.0 (compatible; Iamyukino Check Bot; +https://iamyukino.cn/ask)&quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;符合机器人规范，反爬包含完整请求头（Sec-Fetch-*、DNT、Cache-Control等），降低被拦截风险。爬虫每隔两小时对所有友链进行状态检测。当requests检查失败时，启用退避算法，并尝试启用浏览器模式，提高检测的准确度，支持访问中间跳转页面，跟踪跳转过程。若提供Feed订阅服务，还会每隔两小时获取RSS/Atom的更新信息，在棉花糖站侧边栏和朋友圈等多个页面展示。具体内容详见本文后续部分。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://iamyukino.cn/blog/_astro/p6.DlwtPR1r_Z1rdq7H.webp&quot; alt=&quot;棉花糖站友链&quot; /&gt;
&lt;img src=&quot;https://iamyukino.cn/blog/_astro/p4.D1qwO3dO_Z1oKL0d.webp&quot; alt=&quot;棉花糖站友圈&quot; /&gt;
&lt;img src=&quot;https://iamyukino.cn/blog/_astro/p5.DSG16QrQ_X0i7z.webp&quot; alt=&quot;棉花糖站传送&quot; /&gt;&lt;/p&gt;
&lt;p&gt;基于BootStrap和Masonry的资源站由于之前用的CDN过期忘记续费图片全没了，目前只剩下一个Live2d看板娘页面可见，整体框架是准备重构的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://iamyukino.cn/blog/_astro/p2.DS75uK56_1mbLX1.webp&quot; alt=&quot;资源站&quot; /&gt;&lt;/p&gt;
&lt;p&gt;还有基于Astro+Fuwari搭建的博客站，全自动化构建，分为博客文章和即刻短文两个功能，包含哔哔、番剧、相册等多个特色页面，具体参照Fuwari文档。&lt;/p&gt;
&lt;p&gt;::github{repo=&quot;saicaca/fuwari&quot;}&lt;/p&gt;
&lt;h2&gt;开始&lt;/h2&gt;
&lt;p&gt;本站后端基于Windows Server+Apache24服务器&lt;br /&gt;
文章中所有路径均基于iamyukino.cn，请根据实际情况自行修改，可全局搜索&lt;code&gt;iamyukino(\\?)\.cn&lt;/code&gt;替换为&lt;code&gt;自己的$1.域名&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;将项目复制到 &lt;code&gt;C:\iamyukino.cn&lt;/code&gt;&lt;br /&gt;
修改&lt;code&gt;\apache24\conf\httpd.conf&lt;/code&gt;以下两行为实际Python Home路径&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;LoadFile &quot;C:/Users/Administrator/AppData/Local/Programs/Python/Python313/python313.dll&quot;
WSGIPythonHome &quot;C:/Users/Administrator/AppData/Local/Programs/Python/Python313&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;环境变量&lt;code&gt;Path&lt;/code&gt;新增&lt;code&gt;C:\iamyukino.cn\apache24\bin&lt;/code&gt;
在&lt;code&gt;\apache24\&lt;/code&gt;目录下新建文件夹&lt;code&gt;logs\&lt;/code&gt;并新建文件&lt;code&gt;logs\error.log&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;暂时关闭HTTPS/SSL测试&lt;/h3&gt;
&lt;p&gt;本步骤仅供初次配置使用，后续测试生产环境建议启用HTTPS（如有需要，可启用HSTS），可以在各大云厂商申领免费证书。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;* 若您已有SSL证书，可复制其中Apache版本的四个文件到&lt;code&gt;\apache24\conf\ssl\&lt;/code&gt;目录下，修改&lt;code&gt;\apache24\conf\extra\httpd-vhosts.conf&lt;/code&gt;和&lt;code&gt;\apache24\conf\extra\httpd-ssl.conf&lt;/code&gt;和&lt;code&gt;django\pymain\settings.py&lt;/code&gt;中出现的&lt;code&gt;iamyukino.cn&lt;/code&gt;（正则搜索&lt;code&gt;iamyukino\\?\.cn&lt;/code&gt;）为自己的域名（或ip），即可跳过本步。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;\apache24\conf\httpd.conf&lt;/code&gt;中注释掉以下行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# LoadModule ssl_module modules/mod_ssl.so

# Include conf/extra/httpd-ssl.conf

# &amp;lt;IfModule ssl_module&amp;gt;
# SSLRandomSeed startup builtin
# SSLRandomSeed connect builtin
# &amp;lt;/IfModule&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将&lt;code&gt;apache24\conf\extra\httpd-vhosts.conf&lt;/code&gt;中所有代码注释，然后在末尾加入以下内容：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;VirtualHost *:80&amp;gt;
    ServerName default
    DocumentRoot &quot;${SRVROOT}/htdocs&quot;
    ServerName iamyukino.cn
    ServerAdmin iamyukino@outlook.com
    ErrorLog &quot;${SRVROOT}/logs/error.log&quot;
&amp;lt;/VirtualHost&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;配置数据库&lt;/h3&gt;
&lt;p&gt;下载MySQL 8.1（可顺带安装Navicat Premium Lite）。&lt;br /&gt;
运行MySQL Configurator创建root和iamyukino账号。&lt;br /&gt;
若MySQL Configurator失败，下载依赖&lt;code&gt;https://aka.ms/vs/16/release/vc_redist.x64.exe&lt;/code&gt;。&lt;br /&gt;
环境变量path加&lt;code&gt;安装路径\MySQL\MySQL Server 8.1\bin&lt;/code&gt;。&lt;br /&gt;
修改&lt;code&gt;django\pymain\settings.py&lt;/code&gt;中相关数据库配置。&lt;br /&gt;
然后创建数据库（这里使用用户名为iamyukino）&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;数据库名称：db_iamyukino
字符集：utf8mb4
排序规则：utf8mb4_unicode_ci
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也可以用指令创建&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql -u root -p
CREATE DATABASE db_iamyukino CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
GRANT ALL PRIVILEGES ON db_iamyukino.* TO &apos;iamyukino&apos;@&apos;%&apos;;
FLUSH PRIVILEGES;
exit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;无需创建数据表，Django的ORM会自动帮您创建。&lt;/p&gt;
&lt;h3&gt;配置Django以及测试&lt;/h3&gt;
&lt;p&gt;以管理员打开命令行，输入以下内容。若需使用虚拟环境，请根据实际需要自行调整指令。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pip install django pymysql apscheduler requests feedparser whitenoise
cd c:\iamyukino.cn\django
python manage.py migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注释掉&lt;code&gt;\django\pymain\settings.py&lt;/code&gt;的&lt;code&gt;INSTALLED_APPS = &lt;/code&gt;中的&lt;code&gt;rssfeed&lt;/code&gt;&lt;br /&gt;
在&lt;code&gt;\django\&lt;/code&gt;中新建文件夹&lt;code&gt;statics&lt;/code&gt;&lt;br /&gt;
测试指令，根据报错下载对应包&lt;code&gt;pip install&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python manage.py runserver
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果提示端口被占用，先测试以下指令&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;netstat -ano | findstr :8000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果有输出，则说明确实被占用，可改用&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python manage.py runserver 8080
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;浏览器访问&lt;code&gt;localhost:8080&lt;/code&gt;若看到页面，说明django配置成功。&lt;/p&gt;
&lt;h3&gt;Apache24配置&lt;/h3&gt;
&lt;p&gt;以管理员打开命令行，输入以下内容：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;httpd -k install -n iamyukino
httpd -k start -n iamyukino
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;访问&lt;code&gt;localhost&lt;/code&gt;，可以看到页面正常。
若遇到问题，可输入以下指令卸载&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;httpd -k stop -n iamyukino
httpd -k uninstall -n iamyukino
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;或者重启restart刷新 &lt;code&gt;httpd -k restart -n iamyukino&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Astro配置&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;cd c:\iamyukino.cn\astro\fuwari
npm install -g pnpm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;若npm报错禁止运行脚本，则先输入以下指令再&lt;code&gt;npm install&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Set-ExecutionPolicy RemoteSigned
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;下载完pnpm后，执行以下指令（--max-old-space-size可自行根据内存大小调整）&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pnpm install
pnpm build -- --max-old-space-size=2048
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Umami API 密钥，用于访问 Umami 统计数据&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 如果在 config.ts 中启用了 Umami，建议在此配置 API 密钥
UMAMI_API_KEY=your_umami_api_key_here
# bcrypt 盐值轮数（10-14 推荐，默认 12）
BCRYPT_SALT_ROUNDS=12
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;pinned 字段允许您将重要文章置顶到博客列表的顶部。置顶文章将始终显示在普通文章之前，无论其发布日期如何。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pinned: true  # 将此文章置顶
pinned: false # 普通文章（默认）
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;提示框： 使用&lt;code&gt;&amp;gt; [!NOTE]&lt;/code&gt;、&lt;code&gt;&amp;gt; [!TIP]&lt;/code&gt;、&lt;code&gt;&amp;gt; [!WARNING]&lt;/code&gt;等创建&lt;br /&gt;
数学公式： 使用&lt;code&gt;$行内$&lt;/code&gt;和&lt;code&gt;$$块级$$&lt;/code&gt;语法编写 LaTeX 数学公式&lt;br /&gt;
代码高亮： 高级语法高亮，支持行号和复制按钮&lt;br /&gt;
GitHub 卡片： 使用&lt;code&gt;::github{repo=&quot;用户/仓库&quot;}&lt;/code&gt; 嵌入仓库卡片&lt;/p&gt;
&lt;h3&gt;Feed 爬虫&lt;/h3&gt;
&lt;p&gt;此爬虫每隔两小时爬取所有state不为0的友链，对于提供Feed（RSS/Atom）地址的网站，get请求监测feed地址状态，爬取前十篇文章的相关信息，和数据库比对，进行增量更新。对于未提供Feed地址的网站，通过ping监测站点状态，对可能的异常进行标注。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pip install feedparser python-dateutil apscheduler psutil&lt;/code&gt;&lt;br /&gt;
取消&lt;code&gt;\django\pymain\settings.py&lt;/code&gt;的&lt;code&gt;INSTALLED_APPS = &lt;/code&gt;中的&lt;code&gt;rssfeed&lt;/code&gt; 注释&lt;/p&gt;
&lt;p&gt;修改&lt;code&gt;django/mod_iamyukino/views.py&lt;/code&gt;中的&lt;code&gt;akismet_api_key&lt;/code&gt;为自己的 Akismet Key（必改，可在&lt;a href=&quot;https://akismet.com/&quot;&gt;https://akismet.com/&lt;/a&gt;注册获取您的密钥）&lt;/p&gt;
&lt;p&gt;打开cmd窗口（不能用powershell）复制这两行执行，即可开始定时任务：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd c:\iamyukino.cn\django &amp;amp; python
from rssfeed.commands import start,run,stop,status;run();start();status()
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;from rssfeed.commands import start,run,stop,status
run() # 手动执行一次同步
start() #  开始定时器（两小时）
stop() # 结束定时器
status() # 获取下次执行的时间
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;后续使用&lt;/h3&gt;
&lt;p&gt;更新Django和Apache相关内容后 &lt;code&gt;httpd -k restart -n iamyukino&lt;/code&gt;&lt;br /&gt;
更新Astro相关内容后 &lt;code&gt;pnpm build -- --max-old-space-size=2048&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;每次重启服务器需启动数据库、&lt;code&gt;httpd -k restart -n iamyukino&lt;/code&gt;、然后启动Feed爬虫（启动方法见Feed爬虫一节）&lt;/p&gt;
&lt;p&gt;全局搜索（建议屏蔽dist目录）&lt;code&gt;iamyukino\\?\.cn&lt;/code&gt;替换为自己的域名。&lt;br /&gt;
全局搜索百度统计、Umami统计、百度地图API、Akismet API相关的请自行更换为自己的。&lt;br /&gt;
配置SSL可按需打开前边步骤注释掉的SSL相关内容。&lt;/p&gt;
&lt;p&gt;一些好用的网站（外部链接具有时效性且安全性未知，仅供个人学习参考，雨雪冰屋不对其安全性和可靠性负责）：&lt;br /&gt;
Svg矢量图代码及图标库，很全且搜索方便，可以直接复制Svg代码：&lt;a href=&quot;https://icon-sets.iconify.design/&quot;&gt;https://icon-sets.iconify.design/&lt;/a&gt;&lt;br /&gt;
图片压缩工具网站，本地运行无需上传，且能自行决定压缩比和压缩算法： &lt;a href=&quot;https://squoosh.app/&quot;&gt;https://squoosh.app/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;其余可能用到的指令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 运行pip install mod_wsgi生成.so文件mod_wsgi-express module-config（.pyc后缀改.so）

# httpd 相关
httpd.exe -k install -n &apos;iamyukino&apos;
httpd.exe -k start -n &apos;iamyukino&apos;
httpd.exe -k restart -n &apos;iamyukino&apos;
# net start iamyukino （httpd.exe -k start -n &apos;iamyukino&apos;）
# net stop iamyukino （httpd.exe -k stop -n &apos;iamyukino&apos;）
httpd.exe -k uninstall -n &apos;iamyukino&apos;（httpd.exe -k restart -n &apos;iamyukino&apos;）

# 测试django
where python
python manage.py runserver

# 字体子集压缩
pip install fonttools
pip install brotli
pyftsubset XiaolaiSC.woff2 --text-file=used.txt --output-file=XiaolaiSC.subset.woff2 --flavor=woff2 --no-hinting --desubroutinize
pyftsubset cjkFonts_allseto_v1.11.ttf --text-file=used.txt --output-file=cjkFonts_allseto_v1.11.subset.woff2 --flavor=woff2 --no-hinting --desubroutinize

# 数据库备份
mysqldump -u iamyukino -p db_iamyukino t_ask &amp;gt; t_ask_backup.sql

# 数据库结构调整后
cd c:\iamyukino.cn\django
python manage.py makemigrations mod_iamyukino
python manage.py migrate

# 测试post
curl -X POST https://iamyukino.cn/api/x/submit_ask/ -H &quot;Content-Type: application/json&quot;  -d &quot;{\&quot;nickname\&quot;:\&quot;testuser\&quot;, \&quot;que_text\&quot;:\&quot;Hello, World!\&quot;}&quot;

# Astro
pnpm install &amp;amp; pnpm add sharp # 安装依赖
pnpm preview # 预览
pnpm astro --help # 帮助
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>云函数和负载均衡</title><link>https://iamyukino.cn/blog/posts/serverless-and-load-balance/</link><guid isPermaLink="true">https://iamyukino.cn/blog/posts/serverless-and-load-balance/</guid><description>笔记记录了阿里云ACA认证中VPC配置、Serverless K8s、FC函数计算、无状态应用部署，以及压力测试、HPA策略自动扩缩容等等的过程，比较Serverless和传统VPS的优劣，解决部署AList至云函数遇到的存储配置报错问题。</description><pubDate>Sun, 07 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;负载均衡与弹性伸缩&lt;/h2&gt;
&lt;h3&gt;准备&lt;/h3&gt;
&lt;p&gt;跟随&lt;a href=&quot;https://edu.aliyun.com/certification/aca01&quot;&gt;阿里云ACA认证教程&lt;/a&gt;的后三章对应的知识点文档，我学习了在阿里云平台上给Serverless配置的&lt;a href=&quot;https://edu.aliyun.com/lab/courses/a4f501eb3210445084553ef7ecfd3807/detail&quot;&gt;负载均衡&lt;/a&gt;的步骤，知道了&lt;a href=&quot;https://edu.aliyun.com/lab/courses/38c5ee2ba69445ecb976e5cd0bf3ed4c/detail&quot;&gt;弹性伸缩&lt;/a&gt;和负载均衡的概念。预先完成资源组创建，浏览器控制台里登录进入Serverless的管理界面。&lt;/p&gt;
&lt;h3&gt;引子&lt;/h3&gt;
&lt;p&gt;以前做django博客“友圈”的RSS订阅爬虫、随机友链传送和友链的表单申请，我买的是VPS，然后在其上装apache/django/wsgi/python/mysql等应用运行，开放端口，再设置DNS解析。而Serverless就简单多了，毕竟Server被Less掉了。大致意思是不用管VPS，直接上传代码到平台让平台来部署，绑定域名，直接访问就可以了，轻松很多，而且往往是免费的。&lt;/p&gt;
&lt;p&gt;但这也有一些弊端，阿里云的Serverless限制了总流量，永远只能以一个用户的角度来用，没办法自定义，还会有一些限制，如：CPU最长执行时间 、总请求数、函数总请求数、函数最长执行时间等等需要自己来优化。&lt;/p&gt;
&lt;p&gt;于是弹性伸缩成了优化的关键。我希望通过实操，学习下如何创建一个无需管理底层服务器的Serverless K8s集群和配置步骤，来简化网站框架以及能承受更多流量，还能在空闲时段自降成本的Serverless。&lt;/p&gt;
&lt;p&gt;实操希望通过跟随教程的步骤（设置自动伸缩组、启动模板、伸缩策略、构建自响应负载变化弹性系统），来初步了解云函数、负载均衡、弹性伸缩这些名词的概念和不用买VPS安装宝塔或1Panel再安装Nginx，直接用Serverless云函数免费托管的方法。尤其是vim和部署，这还可以让我熟悉Linux指令系统。&lt;/p&gt;
&lt;h3&gt;实操&lt;/h3&gt;
&lt;p&gt;就像预习报告里说的，Server被Less掉了，直接选“无状态”应用部署方式，简单起见不改YAML，直接创建无状态应用实例，把业务代码上传上去，让平台的CI/CD来负责服务部署，如图所示，:spoiler[好糊什么都看不清，作业要求必须有图只能这样了]&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://iamyukino.cn/blog/_astro/p1.DfGJbvrc_Z1mYoXa.webp&quot; alt=&quot;ServerLess&quot; /&gt;&lt;/p&gt;
&lt;p&gt;举个不恰当的例子就比如我个人网站&amp;lt;a href=&quot;iamyukino.cn&quot; target=&quot;_blank&quot;&amp;gt;iamyukino.cn&amp;lt;/a&amp;gt;的Serverless，首先我将源码上传到边缘安全加速平台EO Pages_腾讯云，再经由平台构建出最终的HTML页面，然后再在平台上绑定域名，直接访问就可以了。&lt;/p&gt;
&lt;p&gt;Serverless的请求独立使得弹性伸缩很容易实现。阿里云平台上，指标弹性伸缩HPA和定时弹性伸缩CronHPA规则这些直接用预设值即可。唯一需要改的是HPA基于CPU利用率的伸缩策略的阈值50%，意思是，当实例平均CPU利用率超过这一值时，系统会自动扩容。也可以把规则改成“基于内存使用量的策略”阈值设为70%。或者CronHPA安装定时伸缩组件，这种方式下我创建了一个每分钟触发一次的规则，实例数量设为扩展至10个。&lt;/p&gt;
&lt;p&gt;阿里云提供了压力测试工具使我可以模拟高负载场景并实时观察实例数量：当CPU或内存使用率超过阈值时，系统自动增加了实例数量，并在控制台的伸缩活动中记录了扩容事件；停止压力测试，负载下降后，系统经过冷却时间自动触发缩容，实例数量恢复至初始值。&lt;/p&gt;
&lt;h3&gt;总结&lt;/h3&gt;
&lt;p&gt;自己个人网站&amp;lt;a href=&quot;iamyukino.cn&quot; target=&quot;_blank&quot;&amp;gt;iamyukino.cn&amp;lt;/a&amp;gt;的django服务器只负责后端api的点赞评论/友圈好文推荐爬虫/随机传送这些功能，其他都前后端分离托管在Vercel上，一超过50人就要考虑负载均衡了。这次实验负载均衡是基于Serverless的，不像传统的VPS，我并不完全拥有它，所以有些服务是不能跑的，特别是在免费层，如特别吃IO和网络的AList，或是并发请求特别高以及商用服务。&lt;/p&gt;
&lt;p&gt;除了阿里云，我了解到一些平台，像Zeabur你可以将自己的VPS托管给Zeabur，它会在你的服务器上安装k3s等服务，之后你就可以直接在Zeabur的仪表盘上进行运维了。在用Serverless的时候，服务直接跑在平台CDN上的，我直接享有所属平台的CDN IP段以及带宽，这也让平台的管理更加方便，不需要大量购入物理机开虚拟机做VPS，只需要做一个小集群并做好用户分配即可。&lt;/p&gt;
&lt;h2&gt;构建虚拟云和云函数&lt;/h2&gt;
&lt;h3&gt;准备&lt;/h3&gt;
&lt;p&gt;跟随阿里云ACA认证教程的前四章，我完成了Apache和Nginx服务安装和AList前端部署，知道了虚拟私有云和云函数FC的大致概念。额外查阅了菜鸟教程，来熟悉Linux指令系统，sudo/ls/cd等一些常用的命令，和vim输入wq保存并退出而不是ctrl+c关掉等内容。&lt;/p&gt;
&lt;h3&gt;计划&lt;/h3&gt;
&lt;p&gt;实操希望通过跟随教程的步骤（配置Apache服务、构建逻辑隔离的VPC环境，还有配置Nginx日志、规划网络地址、创子网、配置路由表和安全组和FC计算这些步骤），来初步了解云计算；具体如阿里云SLS日志服务的安装和配置完全靠指令，还可以让我熟悉Linux系统指令以及Nginx配置指令。
特别注意到几个特殊点：地域和可用区的选择要一致；网段规划避开保留地址段；使用的阿里云FC函数计算，它虽然可以运行二进制文件，但是和传统架构大相径庭，要深入了解。&lt;/p&gt;
&lt;h3&gt;知识点&lt;/h3&gt;
&lt;p&gt;部署完毕前端后，我们就需要一个后端，它需要能够执行AList的二进制文件，并且能开放端口来让前端可以和后端相互通信。以前我的个人网站买的是VPS，直接在服务器上装apache/django/wsgi/python/mysql 等应用运行，或者qqbot用自己的电脑/家里云+Cloudflare Tunnel又或者是Serv00这种免费的托管。&lt;/p&gt;
&lt;p&gt;FC总是在某种条件创建实例来运行服务，也就是说这个实例是无状态的，直接拿来部署AList就会导致第一次配置完毕后过一段时间再访问就会变为初始状态。就算一开始就使用一个全量包来部署，在部署后也无法对其进行更改，绑定NAS来数据持久化。但是NAS文件系统并不能直接绑定到代码的运行时 /code/xxx 目录，查阅教程，了解到可以使用AList的指定配置文件参数，将NAS绑定到 /mnt/AList 然后指定配置文件到 /mnt/AList 。也就是通过./alist server --data /mnt/AList 命令来启动，这样就做到了所谓数据持久化。&lt;/p&gt;
&lt;h3&gt;实操&lt;/h3&gt;
&lt;p&gt;跟随阿里云ACA认证教程的第一章，安装启动Apache、设开机自启，还有查运行状态等指令。额外查阅了菜鸟教程，来熟悉Linux指令系统，sudo/ls/cd等一些常用的命令，和vim输入wq保存并退出而不是ctrl+c关掉等内容。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://iamyukino.cn/blog/_astro/p2.DRpvDLn1_1tGHDS.webp&quot; alt=&quot;ECS&quot; /&gt;&lt;/p&gt;
&lt;p&gt;阿里云控制台已经帮我们创建好了名为vpc-test的专有网络，也已创建对应的交换机vswitch-1。资源在华东2上海。在UI界面创建新的安全组vpc-test-sg，将它关联到新建的VPC。安全组用预设即可：允许内网通信但限制外部访问。&lt;/p&gt;
&lt;p&gt;创建好了之后停止实例运行解绑弹性IP，更换专有网络的功能将实例迁移到vpc-test环境，重启获得新的内网IP地址完成迁移。&lt;/p&gt;
&lt;p&gt;第三章学习SSH连接互联网访问和内网连通性，网络安全隔离。通过包管理安装了Logtail日志采集客户端，配置接入日志库。试着用浏览器访问服务器IP地址并刷新多次来模拟产生文本日志，完成测试。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://iamyukino.cn/blog/_astro/p3.CA7ce1Iz_1x8q1G.webp&quot; alt=&quot;monitor&quot; /&gt;&lt;/p&gt;
&lt;p&gt;预习已部署完AList前端，通信地址在根目录的env.production里定义。我们目前并不知道这个后端URL如何填写，因为这个URL是由阿里云在FC函数创建完毕的时候才会展示，所以我们暂且搁置到一遍。进入到阿里云FC页，进入创建Web函数的页面，函数名写AList，选5244和从文件夹上传代码到函数，也就是AList的二进制文件，解压创建一个空文件夹放进去，然后上传这个文件夹到函数。齐老师特别强调不需要清理资源，因为这样可以更快进入下一次实验。&lt;/p&gt;
&lt;h3&gt;总结&lt;/h3&gt;
&lt;p&gt;这次实验，我了解了虚拟私有云的大致概念，从网络规划到资源创建，从实例迁移到功能验证，我熟悉云网络建设的一些内容，特别是可用区一致性、网段规划这些。我还额外了解了阿里云FC函数计算，它虽然可以运行二进制文件，但是和传统架构大相径庭。&lt;/p&gt;
&lt;p&gt;特别遇到一个问题，FC函数计算的配置里提示获取设置失败：请稍后，正在加载储存。有一段时间没打开网页，就会提示这个。其实就是字面意思，报错加载存储需要时间，不过等半天也未必加载出来，直接进后台管理页面：&amp;lt;a href=&quot;javascript:;&quot;&amp;gt;http://alist地址:5244/@manage/settings/site&amp;lt;/a&amp;gt;把阿里相关存储都删掉，刷新下页面就进去了。&lt;/p&gt;
</content:encoded></item><item><title>云计算知识点总结</title><link>https://iamyukino.cn/blog/posts/cloud-computing/</link><guid isPermaLink="true">https://iamyukino.cn/blog/posts/cloud-computing/</guid><description>云计算期末复习题库 A/B卷</description><pubDate>Sat, 06 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;云计算期末复习题库 A&lt;/h2&gt;
&lt;h3&gt;单项选择题&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;A1 - 在云计算中，弹性是指什么？&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;× 云服务的高可用性
× 云服务的安全性
× 数据在不同区域的复制
√[根据需求动态调整资源的能力]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;A2 - 以下哪种云计算部署模型允许多个组织共享同一个云基础设施，但数据和应用程序彼此隔离？&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;× 公有云
× 私有云
× 混合云
√[社区云]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;A3 - 在云计算中，虚拟化技术的主要作用是什么？&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;× 提高物理服务器的处理速度
√[将物理资源抽象为逻辑资源，实现资源的灵活分配和隔离]
× 增强网络的安全性
× 减少数据中心的能耗
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;A4 - 在云计算环境中，CAP 定理指出分布式系统无法同时满足以下哪三项特性？&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;√[一致性、可用性、分区容错性]
× 一致性、可扩展性、持久性
× 可用性、可靠性、可维护性
× 分区容错性、可扩展性、安全性
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;A5 - 以下哪种技术不是用于实现云计算中的虚拟化？&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;× 容器化技术
× 虚拟机技术
√[大数据分析技术]
× 网络虚拟化技术
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;简答题&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;A_S1 - 解释什么是云计算？&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;基于互联网的计算模式
按需提供计算与存储服务
无需管理物理设备，具备可扩展与灵活性
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;A_S2 - 解释 IaaS、PaaS、SaaS 各自的含义。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;IaaS：提供处理/存储/网络，用户管理 OS 与应用
PaaS：提供开发环境，用户管理应用与数据
SaaS：提供应用服务，用户无需管理底层
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;A_S3 - 解释专有网络（VPC）。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;用户专有的云上私有网络
可控 IP 范围、路由表、网关
通过高速通道连接其他网络，支持混合云
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;A_S4 - 传统服务器与云服务器的区别。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;传统：独立硬件、成本高、扩展难
云服务器：虚拟化池化、按需付费、弹性扩展
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;案例分析题&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;A_C1 - 分析云计算安全措施中实现容灾场景方案（ECS/SLB/RDS/OSS）。
&amp;lt;svg viewBox=&quot;0 0 700 320&quot; class=&quot;dr-svg&quot;&amp;gt;&amp;lt;rect x=&quot;40&quot; y=&quot;140&quot; width=&quot;120&quot; height=&quot;60&quot; rx=&quot;8&quot; fill=&quot;#ffffff&quot; stroke=&quot;#2563eb&quot; stroke-width=&quot;3&quot;&amp;gt;&amp;lt;/rect&amp;gt;&amp;lt;text x=&quot;100&quot; y=&quot;175&quot; text-anchor=&quot;middle&quot;&amp;gt;SLB&amp;lt;/text&amp;gt;&amp;lt;rect x=&quot;240&quot; y=&quot;60&quot; width=&quot;120&quot; height=&quot;60&quot; rx=&quot;8&quot; fill=&quot;#ffffff&quot; stroke=&quot;#16a34a&quot;&amp;gt;&amp;lt;/rect&amp;gt;&amp;lt;text x=&quot;300&quot; y=&quot;95&quot; text-anchor=&quot;middle&quot;&amp;gt;ECS-A&amp;lt;/text&amp;gt;&amp;lt;rect x=&quot;240&quot; y=&quot;220&quot; width=&quot;120&quot; height=&quot;60&quot; rx=&quot;8&quot; fill=&quot;#ffffff&quot; stroke=&quot;#16a34a&quot;&amp;gt;&amp;lt;/rect&amp;gt;&amp;lt;text x=&quot;300&quot; y=&quot;255&quot; text-anchor=&quot;middle&quot;&amp;gt;ECS-B&amp;lt;/text&amp;gt;&amp;lt;rect x=&quot;460&quot; y=&quot;140&quot; width=&quot;120&quot; height=&quot;60&quot; rx=&quot;8&quot; fill=&quot;#ffffff&quot; stroke=&quot;#2563eb&quot; stroke-width=&quot;3&quot;&amp;gt;&amp;lt;/rect&amp;gt;&amp;lt;text x=&quot;520&quot; y=&quot;175&quot; text-anchor=&quot;middle&quot;&amp;gt;RDS&amp;lt;/text&amp;gt;&amp;lt;rect x=&quot;600&quot; y=&quot;20&quot; width=&quot;80&quot; height=&quot;40&quot; rx=&quot;8&quot; fill=&quot;#ffffff&quot; stroke=&quot;#2563eb&quot; stroke-width=&quot;3&quot;&amp;gt;&amp;lt;/rect&amp;gt;&amp;lt;text x=&quot;640&quot; y=&quot;45&quot; text-anchor=&quot;middle&quot;&amp;gt;OSS&amp;lt;/text&amp;gt;&amp;lt;path d=&quot;M 160 170 L 240 90&quot; stroke=&quot;#2563eb&quot; fill=&quot;none&quot; stroke-width=&quot;2&quot; stroke-dasharray=&quot;300&quot; stroke-dashoffset=&quot;0&quot; style=&quot;transition: stroke-dashoffset 0.8s;&quot;&amp;gt;&amp;lt;/path&amp;gt;&amp;lt;path d=&quot;M 160 170 L 240 250&quot; stroke=&quot;#2563eb&quot; fill=&quot;none&quot; stroke-width=&quot;2&quot; stroke-dasharray=&quot;300&quot; stroke-dashoffset=&quot;0&quot; style=&quot;transition: stroke-dashoffset 0.8s;&quot;&amp;gt;&amp;lt;/path&amp;gt;&amp;lt;path d=&quot;M 360 90 L 460 170&quot; stroke=&quot;#2563eb&quot; fill=&quot;none&quot; stroke-width=&quot;2&quot; stroke-dasharray=&quot;300&quot; stroke-dashoffset=&quot;0&quot; style=&quot;transition: stroke-dashoffset 0.8s;&quot;&amp;gt;&amp;lt;/path&amp;gt;&amp;lt;path d=&quot;M 360 250 L 460 170&quot; stroke=&quot;#2563eb&quot; fill=&quot;none&quot; stroke-width=&quot;2&quot; stroke-dasharray=&quot;300&quot; stroke-dashoffset=&quot;0&quot; style=&quot;transition: stroke-dashoffset 0.8s;&quot;&amp;gt;&amp;lt;/path&amp;gt;&amp;lt;path d=&quot;M 520 140 L 640 60&quot; stroke=&quot;#2563eb&quot; fill=&quot;none&quot; stroke-width=&quot;2&quot; stroke-dasharray=&quot;300&quot; stroke-dashoffset=&quot;0&quot; style=&quot;transition: stroke-dashoffset 0.8s;&quot;&amp;gt;&amp;lt;/path&amp;gt;&amp;lt;/svg&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;原答案（拼凑）：前端 SLB 流量分发；ECS 多可用区集群部署；RDS 主从热备；OSS 多副本存储。情景：给出架构图，包含 ECS、SLB、RDS、OSS，分布在可用区 A 和 B。正常状态 (0-10s): 流量从顶部的 SLB 均匀流入 A 和 B 的 ECS，最终汇入 A 的 RDS 主库。数据流线显示从 A 主库同步到 B 备库。

补充（供参考）：正常状态下，流量经SLB分流至双AZ的ECS，数据写入A区RDS主库并同步至B区备库，OSS支撑资源访问。A区故障时，SLB健康检查自动将流量切至B区ECS，RDS备库（B区）升为主库承接写入，OSS多副本保障访问不中断，故障恢复后原A区主库作为备库重新同步，最终实现系统的容灾能力。
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;计算题&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;A_M1 - ESSD PL1 云盘性能计算（容量 5000 GiB）。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;IOPS = min{1800 + 50 × Capacity, 50000}
Throughput = min{120 + 0.5 × Capacity, 350}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;云计算期末复习题库 B&lt;/h2&gt;
&lt;h3&gt;单项选择题&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;B1 - 云计算的特性包括？&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;× 简便的访问
× 高可信度与经济性
× 按需计算与服务
√[以上全部]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;B2 - &quot;云&quot;服务影响包括？&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;× 理财
× 健康
× 交通导航
√[以上全部]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;B3 - 云计算是对（）技术的发展与运用？&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;× 并行计算
× 网格计算
× 分布式计算
√[三个选项都是]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;B4 - IaaS是（）的简称。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;× 软件即服务
× 平台即服务
√[基础设施即服务]
× 硬件即服务
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;B5 - 不属于桌面虚拟化技术构架的选项是？&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;× VDI
× VOI
√[远程托管桌面]
× OSV 智能桌面虚拟化
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;问答题&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;B_Q1 - 简述 Hadoop, Spark, MPI 三种计算框架的特点及适用场景。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;Hadoop：基于磁盘、批处理；适合大规模数据，SPMD
Spark：基于内存、迭代计算；适合需要多轮迭代的应用
MPI：消息传递、复杂；适合各种复杂并行计算，MPMD
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;B_Q2 - 详述云服务的基本层次。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;IaaS：提供核心计算/网络 (EC2)
PaaS：提供开发平台 (GAE)
SaaS：提供 Web 软件 (Google Docs)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;应用分析题&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;B_CAP1 - CAP 定理应用：v1 正在对 v0 赋值时是否可同时保证一致性与可用性？
&amp;lt;svg viewBox=&quot;0 0 600 100&quot; class=&quot;cap-svg&quot;&amp;gt;&amp;lt;g&amp;gt;&amp;lt;circle cx=&quot;150&quot; cy=&quot;50&quot; r=&quot;24&quot; fill=&quot;#ffffff&quot; stroke=&quot;#e5e7eb&quot;&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;text x=&quot;150&quot; y=&quot;55&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot;&amp;gt;G1&amp;lt;/text&amp;gt;&amp;lt;/g&amp;gt;&amp;lt;g&amp;gt;&amp;lt;circle cx=&quot;450&quot; cy=&quot;50&quot; r=&quot;24&quot; fill=&quot;#ffffff&quot; stroke=&quot;#16a34a&quot; stroke-width=&quot;3&quot;&amp;gt;&amp;lt;/circle&amp;gt;&amp;lt;text x=&quot;450&quot; y=&quot;55&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot;&amp;gt;G2&amp;lt;/text&amp;gt;&amp;lt;/g&amp;gt;&amp;lt;path d=&quot;M 174 50 L 426 50&quot; stroke=&quot;#2563eb&quot; fill=&quot;none&quot; stroke-width=&quot;2&quot; stroke-dasharray=&quot;400&quot; stroke-dashoffset=&quot;0&quot; style=&quot;transition: stroke-dashoffset 0.8s;&quot;&amp;gt;&amp;lt;/path&amp;gt;&amp;lt;/svg&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;原答案（拼凑）：不可同时。如果你想让 v0 随时能回答查询（保证可用性），那么在 v1 更新但还没来得及通知 v0 的这段时间里，v0 只能回答旧值（这就牺牲了一致性）。假设你想把 v1 的值更新为 5，并且要求 v0 也能读到 5（一致性）。（则v0需“锁住等待”同步完成，期间无法响应其他请求，牺牲可用性）。既然网络故障（分区）无法避免，你就必须在‘锁住等待’和‘回答旧值’之间做选择。所以，一致性和可用性不可兼得。

备注（供参考）：根据CAP定理，分布式系统中分区容错性（P）是必须容忍的前提（网络故障客观存在），因此只能在一致性（C）和可用性（A）间二选一。当v1对v0赋值时，若优先保证可用性（v0随时响应查询），因数据同步可能存在延迟（如网络分区未恢复），v0在收到v1更新前只能返回旧值，牺牲一致性；若优先保证一致性（v0必须读到v1的新值），则v0需“锁住等待”同步完成，期间无法响应其他请求，牺牲可用性。
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;计算题&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;B_M1 - 100 Mbps 下载 1 GB 文件的时间计算。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;1 GB = 1024 MB
100 Mbps = 12.5 MB/s
Time = Size / Speed = 1024 / 12.5 = 81.92 s
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>【日志】决定将这个blog迁移到astro！</title><link>https://iamyukino.cn/blog/posts/migrated-to-astro/</link><guid isPermaLink="true">https://iamyukino.cn/blog/posts/migrated-to-astro/</guid><description>Migrated to astro！旧版内容将会有选择性地同步到这里。</description><pubDate>Tue, 07 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;决定将网站的博客页迁移到 &lt;a href=&quot;https://astro.build&quot;&gt;astro&lt;/a&gt; !&lt;br /&gt;
之前的内容在不久的将来会（有选择性地）同步到这里&lt;br /&gt;
使用的主题是：&lt;/p&gt;
&lt;p&gt;::github{repo=&quot;saicaca/fuwari&quot;}&lt;/p&gt;
&lt;p&gt;这个主题可以说是新时代的 &lt;a href=&quot;https://hexo.io/zh-cn/&quot;&gt;Hexo&lt;/a&gt;（如雨雪之前用的 &lt;a href=&quot;https://github.com/honjun/hexo-theme-sakura&quot;&gt;Sakura 主题&lt;/a&gt;），事实证明也确实配的上“博客标准主题”，可读性和设计都挺不错的，反观 heo 系有很多设计师自己的个性。如果您也想拥有一个类似功能和样式的博客，这里推荐一下基于 &lt;a href=&quot;https://github.com/saicaca/fuwari&quot;&gt;fuwari&lt;/a&gt; 二次创作的 astro 主题 Mizuki&lt;/p&gt;
&lt;p&gt;::github{repo=&quot;matsuzaka-yuki/mizuki&quot;}&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://iamyukino.cn/blog/_astro/p1.DgwMHT4R_WULoA.webp&quot; alt=&quot;MizukiReview&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>在Minecraft BE搭建神经网络？</title><link>https://iamyukino.cn/blog/posts/digit-recognition-in-mcbe/</link><guid isPermaLink="true">https://iamyukino.cn/blog/posts/digit-recognition-in-mcbe/</guid><description>PyTorch训练，Minecraft模拟前向传播，实现简易的手写数字识别功能。</description><pubDate>Mon, 28 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;iframe width=&quot;100%&quot; height=&quot;468&quot; src=&quot;//player.bilibili.com/player.html?bvid=BV1Uq8Gz6Eyr&amp;amp;p=1&quot; scrolling=&quot;no&quot; border=&quot;0&quot; frameborder=&quot;no&quot; framespacing=&quot;0&quot; allowfullscreen=&quot;true&quot;&amp;gt; &amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;p&gt;:::TIP[红石手写数字识别（网易手机版）]
第一版完成～ &amp;lt;a href=&quot;https://iamyukino.cn/download/x/mc_digital_recog_v1.zip&quot; target=&quot;_blank&quot;&amp;gt;点我下载存档&amp;lt;/a&amp;gt; 数电萌新的第一个作品！
:::&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用PyTorch训练一个单层感知机（数据用二值化17x17的MINST）将参数按比例（/3.11）缩放并四舍五入，使权重和偏置在[-5, 5]区间内。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;::github{repo=&quot;iamyukino/mc_digital_recog&quot;}&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;数电全程仅整数相乘相加（无反向传播），输入17x17输出10全连接（2890权重+10偏置共2900个4bit参数无隐藏层无卷积），相对简单适合红石数电入门。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可在此基础上加一层10个节点的隐藏层扩展为多层感知机（见 &amp;lt;a href=&quot;https://www.youtube.com/watch?v=DQ0lCm0J3PM&quot; target=&quot;_blank&quot;&amp;gt;单隐藏层红石MLP网络&amp;lt;/a&amp;gt;）已经在改了，不过由于网易比较器会异常高频，接口也比较封闭，改起来可能有点麻烦。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>基于 mclib 的剪切板管理器 clipboard-master</title><link>https://iamyukino.cn/blog/posts/clipboard-master/</link><guid isPermaLink="true">https://iamyukino.cn/blog/posts/clipboard-master/</guid><description>想要在电脑上复制多段文字却又无能为力？学校 C++ 课程作业，向您展现 mclib 的基础用法。</description><pubDate>Thu, 30 Nov 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;项目简介&lt;/h2&gt;
&lt;p&gt;这是一个复刻了小艺输入法剪切板功能的简易剪切板管理系统，解决了在 Win11 中难以查看剪切板历史记录的问题。项目围绕 C语言大作业 要求的 “增、删、改、查” 四个功能展开，依次实现。&lt;/p&gt;
&lt;p&gt;编译环境：MSVC/GCC + &lt;a href=&quot;https://github.com/iamyukino/mclib&quot;&gt;mclib&lt;/a&gt; + Win2000 + C++11&lt;/p&gt;
&lt;h2&gt;程序简介&lt;/h2&gt;
&lt;h3&gt;运行截图&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://iamyukino.cn/blog/_astro/p1.nx-a_NBi_Z1kumF0.webp&quot; alt=&quot;运行截图&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;软件下载&lt;/h3&gt;
&lt;p&gt;&amp;lt;a href=&quot;https://iamyukino.cn/download/x/clipboard_master.exe&quot; target=&quot;_blank&quot;&amp;gt;点我下载 clipboard-master.exe&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;h3&gt;使用方法&lt;/h3&gt;
&lt;p&gt;用户 (通过右键或快捷键等方式) 复制的内容将自动记录到程序中。用户可以通过将光标移动指定位置，并单击需要粘贴的内容，来完成粘贴。每条剪切板记录右侧两个按钮 &lt;code&gt;Mod&lt;/code&gt; &lt;code&gt;X&lt;/code&gt; 提供修改和删除功能。通过单击顶部标题栏下方两个按钮 &lt;code&gt;搜索&lt;/code&gt; &lt;code&gt;清空&lt;/code&gt; 使用查询和清空功能。&lt;/p&gt;
&lt;h3&gt;实现细节&lt;/h3&gt;
&lt;p&gt;在数据结构上，项目选择了内存管理优化过的链式循环队列。在查询方面，一开始选择了关键词提取匹配算法，但由于较为复杂编写难度大，改为使用最长公共子串模板。在 GUI 方面，使用了自己独立开发的图形库 &lt;a href=&quot;https://github.com/iamyukino/mclib&quot;&gt;mclib&lt;/a&gt;，由于是自己造的轮子，对实现细节较为熟悉，遇到问题能够快速定位解决。&lt;/p&gt;
&lt;h2&gt;完善和优化&lt;/h2&gt;
&lt;p&gt;此选题目的和初衷是为了更方便地进行复制和粘贴。&lt;/p&gt;
&lt;p&gt;程序使用 Github 进行版本控制，并邀请了几位同学参与测试，提出了一些建议，例举部分如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;原本软件的粘贴功能通过更新剪切板实现，但部分网址（例如：头歌等）禁止使用复制和粘贴功能。&lt;br /&gt;
解决方案：通过改用 &lt;code&gt;SendInput&lt;/code&gt; 模拟发送按键。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;剪切板要修改已经复制好的内容，给出的修改框太小了，有时候不能把内容显示完全。&lt;br /&gt;
解决方案：输入框独立弹出，并支持改变窗口大小。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;部分软件一次复制操作会触发多次剪切板内容变化事件。&lt;br /&gt;
解决方案：在将新内容加入剪切板队列时先去重，这样还能保证新复制的内容在最前面。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;已经复制的内容，如果下一次只复制其中一部分没办法实现，只能通过复制修改框内的内容，较为繁琐。&lt;br /&gt;
解决方案：有待进一步实现。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;已知部分软件（例如：Edge142等）复制时不触发 &lt;code&gt;ClipboardUpdate&lt;/code&gt; 事件。&lt;br /&gt;
解决方案：未定位问题，有待修复。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;源代码&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;// main.cpp
#include &quot;afx.h&quot;
auto title = &quot;剪切板管理系统&quot;;

int main() {
    afx::startup (title);
    while (1) {
        afx::peekmsg ();
        afx::print ();
    }
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;// afx.h
#include &quot;mclib/src/mclib.h&quot;
#include &amp;lt;string&amp;gt;
using namespace std;
using namespace mcl;
namespace afx {
    /* gui */
    void startup(char const* caption);
    void peekmsg();
    void print();
    int print_box(point1d_t y, string str, bool isnearest);
    int print_title();
 
    double algo_lwst(wstring&amp;amp; a, wstring b); /* lg common substr */
    void paste(std::wstring&amp;amp; str);
 
    enum :int { TW = 7 };
    /* data-structure */
    class sys_t {
    public:
        /* linked-queue node */
        class lqnode_t {
        public:
            string data;
            lqnode_t* next = nullptr;
        };
    public:
        sys_t(); /* create head node */
        ~sys_t(); /* release lkqueue */
		bool prepush(string rhs); /* remove duplicate */
        bool push (string rhs); /* add node */
        void pop_next(lqnode_t*&amp;amp; rhs);
    public:
        int MX = 32;
        int _len = 0; /* length */
        lqnode_t* _r = 0; /* rear */
        lqnode_t* _f = 0; /* front */
    public:
        void* hwndfc = 0;
    public:
        lqnode_t** _pointdelete = 0;
        lqnode_t** _pointed = 0;
        lqnode_t** _pointmodify = 0;
        lqnode_t** _pointnearest = 0;
        point1d_t sw = ((TW &amp;lt;&amp;lt; 2) + TW);
        point1d_t sx = 0;
        point1d_t sy = (TW &amp;lt;&amp;lt; 1) + TW;
        bool b_inempty = false;
        bool b_insearch = false;
        char : 8; char : 8;
    };
    extern sys_t sys;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;// afx.cpp
#include &quot;afx.h&quot;
#include &quot;inputbox.h&quot;
#include &quot;mclib/cpp/mcl_base.h&quot;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;cmath&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#pragma warning(disable: 5039 4996)
#define WIN32_LEAN_AND_MEAN
# include &amp;lt;windows.h&amp;gt;
#undef WIN32_LEAN_AND_MEAN
namespace afx {
    sys_t sys;
    sys_t::sys_t() 
      : _r (new (std::nothrow) lqnode_t()) {
        if (!_r) return;
        _f = _r;
        _r -&amp;gt; next = nullptr;
 
        FILE* fp = 0;
        fp = fopen(&quot;.bin&quot;, &quot;r&quot;);
        if (!fp) return ;
 
        string s; int c;
        while (~(c = fgetc(fp))) {
            if (!c) push(s), s.clear();
            else s.push_back(char(c));
        }
        fclose(fp);
    }
    sys_t::~sys_t() {
        FILE* fp = 0;
        fp = fopen (&quot;.bin&quot;, &quot;w&quot;);
 
        /* Thanks to @djw for submitting this issue! Corrected. */
        while (_f) {
            _r = _f;
            _f = _f -&amp;gt; next;
            delete _r;
            if (_len &amp;amp;&amp;amp; fp) fputs(_f-&amp;gt;data.c_str(), fp), fputc(0, fp), --_len;
        }
 
        if (fp) fclose(fp);
        _f = nullptr;
    }
    bool sys_t::prepush(string rhs) {
        bool removed = false;
        prepush(rhs);
        if (!_f || !_f-&amp;gt;next) return removed;
        lqnode_t* prev = _f;
        lqnode_t* curr = _f-&amp;gt;next;
        while (curr) {
            if (curr-&amp;gt;data == rhs) {
                lqnode_t* to_delete = curr;
                prev-&amp;gt;next = curr-&amp;gt;next;
                if (curr == _r) {
                    _r = prev;
                    _r-&amp;gt;next = nullptr;
                }
                curr = curr-&amp;gt;next;
                delete to_delete;
                --_len;
                removed = true;
            }
            else {
                prev = curr;
                curr = curr-&amp;gt;next;
            }
        }
        return removed;
    }
    bool sys_t::push(string rhs) {
        if (!_f) return 0;
        if (_len == MX) {
        /* linkedqueue is full of msg */
            lqnode_t* t = _f -&amp;gt; next -&amp;gt; next;
            _f -&amp;gt; next -&amp;gt; next = _r -&amp;gt; next;
            _r -&amp;gt; next = _f -&amp;gt; next;
            _r = _r -&amp;gt; next;
            _f -&amp;gt; next = t;
 
            _r -&amp;gt; next = nullptr;
            _r -&amp;gt; data = rhs;
            return 1;
        }
        if (!_r -&amp;gt; next) {
        /* never got enough length */
            lqnode_t* node = new (std::nothrow) lqnode_t();
            if (!node)
                return 0;
 
            ++ _len;
            _r -&amp;gt; next = node;
            _r = node;
 
            _r -&amp;gt; next = nullptr;
            _r -&amp;gt; data = rhs;
            return 1;
        }
        /* has got enough length */
        ++ _len;
        _r = _r -&amp;gt; next;
 
        _r -&amp;gt; data = rhs;
        return 1;
    }
    void sys_t::
    pop_next (lqnode_t*&amp;amp; rhs){
        -- _len;
        if (rhs -&amp;gt; next == _r) {
            _r = rhs;
            return ;
        }
        lqnode_t* t = rhs -&amp;gt; next -&amp;gt; next;
        rhs -&amp;gt; next -&amp;gt; next = _r -&amp;gt; next;
        _r -&amp;gt; next = rhs -&amp;gt; next;
        rhs -&amp;gt; next = t;
    }
 
    /* init window */
    void startup(char const* caption) {
        key.set_repeat(true);
        auto size = display.get_desktop_size();
        display.set_mode({ size.x / 5, size.y / 5 }, dflags.Resizable | dflags.DoubleBuf | dflags.AlphaWindow);
        display.set_window_pos({ size.x - (size.x / 4), size.y - (size.y / 3) }, true);
        display.set_caption(caption);
        if (!display) exit(0);
        display.get_surface().set_alpha(198);
        display.flip();
        display.get_surface().set_alpha(198);
    }
    /* peek msg loop */
    void peekmsg() {
        static tclock_t ck;
        string str;
        ck.tick();
        HWND hwnd = ::GetForegroundWindow();
        if (hwnd != display.get_wm_info()[&quot;window&quot;])
            sys.hwndfc = hwnd;
        for (auto&amp;amp; ev : event.get()) {
            switch (ev.type) {
            case event.Quit: exit(0);
            case event.ClipboardUpdate: {
                str = event.get_details_clipboard_a();
                if (str.size())
                    sys.push(str);
                break;
            }
            case event.MouseWheel: {
                if (ev.wheel.delta.y &amp;lt; 0) {
                    point1d_t ssm = sys.sw * sys._len - min(display.get_window_size().y - sys.sy - TW, sys.sw * sys._len);
                    if (sys.sx + TW &amp;lt;= ssm) sys.sx += TW;
                    else sys.sx = ssm;
                }
                else {
                    if (sys.sx - TW &amp;gt;= 0) sys.sx -= TW;
                    else sys.sx = 0;
                }
                break;
            }
            case event.KeyDown: {
                if (ev.key.key == key.VkDown) {
                    point1d_t ssm = sys.sw * sys._len - min(display.get_window_size().y - sys.sy, sys.sw * sys._len);
                    if (sys.sx + TW &amp;lt;= ssm) sys.sx += TW;
                    else sys.sx = ssm;
                }
                if (ev.key.key == key.VkUp) {
                    if (sys.sx - TW &amp;gt;= 0) sys.sx -= TW;
                    else sys.sx = 0;
                }
                break;
            }
            case event.MouseButtonUp: {
                if (ev.mouse.button != mouse.BtnLButton) break;
                rect_t rc = display.get_window_rect();
 
                if (afx::sys.b_inempty) {
                    sys._r = sys._f;
                    sys._len = 0;
                    sys._pointdelete = 0;
                    sys._pointed = 0;
                    sys._pointmodify = 0;
                    sys._pointnearest = 0;
                }
                if (afx::sys.b_insearch) {
                    if (!sys._len) break;
                    string lhs = input_box(0, const_cast&amp;lt;TCHAR*&amp;gt;(_T(&quot;查找（匹配相似度最高的元素）&quot;)), 0, rc.x, rc.y);
                    mcl::mcl_simpletls_ns::mcl_m2w_str_t wlhs(lhs.c_str());
                    wstring lwlhs = (wchar_t*)(wlhs);
                    if (!lhs.size()) break;
                    double maxn = -1, n = 0;
                    int i = 0, j = 0;
                    auto** mint = &amp;amp;afx::sys._f;
                    for (auto** it = &amp;amp;afx::sys._f; *it != afx::sys._r; it = &amp;amp;(*it)-&amp;gt;next, j++){
                        mcl::mcl_simpletls_ns::mcl_m2w_str_t wrhs((*it)-&amp;gt;next-&amp;gt;data.c_str());
                        n = algo_lwst(lwlhs, wstring((wchar_t*)wrhs));
                        if (n &amp;gt;= maxn) maxn = n, mint = it, i = j;
                    }
                    if (maxn &amp;lt; 1) {
                        MessageBoxCentered(NULL, _T(&quot;什么都没有找到 QAQ\n真的，一个字都没有...&quot;), _T(&quot;查找（匹配相似度最高的元素）&quot;), MB_OK);
                        break;
                    }
                    sys._pointnearest = mint;
                    point1d_t y = sys.sy + (sys._len - i - 1) * sys.sw - sys.sx;
                    point2d_t sz = display.get_window_size();
                    rect_t boxrc_out = { TW, y, rc.x - TW - TW, (TW &amp;lt;&amp;lt; 2) + 4 };
                    rect_t boxrc_in = { boxrc_out.x + 1, boxrc_out.y + 1, boxrc_out.w - 2, boxrc_out.h - 2 };
                    rc = {0, sys.sy, sz.x, sz.y};
                    if (boxrc_in.x &amp;lt; rc.x || boxrc_in.y &amp;lt; rc.y || boxrc_in.x + boxrc_in.w &amp;gt; rc.x + rc.w || boxrc_in.y + boxrc_in.h &amp;gt; rc.y + rc.h) {
                        sys.sx = (sys._len - i - 1) * afx::sys.sw;
                        point1d_t ssm = sys.sw * sys._len - min(display.get_window_size().y - sys.sy, sys.sw * sys._len);
                        if (sys.sx &amp;gt; ssm) sys.sx = ssm;
                    }
                }
                else if (sys._pointdelete) {
                    if (sys._pointed == sys._pointdelete) sys._pointed = 0;
                    if (sys._pointmodify == sys._pointdelete) sys._pointmodify = 0;
                    if (sys._pointnearest == sys._pointdelete) sys._pointnearest = 0;
                    sys.pop_next(*sys._pointdelete);
                }
                else if (sys._pointmodify) {
                    char* p = input_box(0, const_cast&amp;lt;TCHAR*&amp;gt;(_T(&quot;修改（更改指定的元素）&quot;)), const_cast&amp;lt;char*&amp;gt;((*sys._pointmodify)-&amp;gt;next-&amp;gt;data.c_str()), rc.x, rc.y);
                    if (p[0]) (*sys._pointmodify) -&amp;gt; next -&amp;gt; data = p;
                }
                break;
            }
            case event.MouseButtonDown: {
               if (sys._pointed) {
                   if (sys.hwndfc) {
                       ::SwitchToThisWindow(HWND(sys.hwndfc), TRUE);
                       mcl::mcl_simpletls_ns::mcl_m2w_str_t ws((*sys._pointed)-&amp;gt;next-&amp;gt;data.c_str());
                       wstring ws2 = static_cast&amp;lt;wchar_t*&amp;gt;(ws);
                       paste(ws2);
                       /* for (wchar_t c : ws2) */
                       /*     ::PostMessage(HWND(sys.hwndfc), WM_IME_CHAR, c, 0); */
                   }
               }
               break;
            }
            }
        }
    }
    void print() {
        display.get_surface().fill(rgba(255, 222, 222, 50));
        int i = 0, ret = 0;
        if (afx::sys._len) {
            for (auto** it = &amp;amp;afx::sys._f; *it != afx::sys._r; it = &amp;amp;(*it)-&amp;gt;next, ++i) {
                ret = afx::print_box(afx::sys.sy + (afx::sys._len - i - 1) * afx::sys.sw - afx::sys.sx, (*it)-&amp;gt;next-&amp;gt;data.c_str(), it == sys._pointnearest);
                if (sys._pointdelete &amp;amp;&amp;amp; (*it)-&amp;gt; next == (*sys._pointdelete) -&amp;gt; next &amp;amp;&amp;amp; ret != 2) sys._pointdelete = 0;
                else if (ret == 2) sys._pointdelete = it;
                if (sys._pointed == it &amp;amp;&amp;amp; ret != 1) sys._pointed = 0;
                else if (ret == 1) sys._pointed = it;
                if (sys._pointmodify == it &amp;amp;&amp;amp; ret != 3) sys._pointmodify = 0;
                else if (ret == 3) sys._pointmodify = it;
            }
        }
        ret = afx::print_title();
        afx::sys.b_inempty = ret == 1;
        afx::sys.b_insearch = ret == 2;
        display.flip();
    }
 
    int print_box (point1d_t y, string str, bool isnearest) {
        point2d_t wndsz = display.get_window_size();
        point2d_t ms = mouse.get_pos();
        rect_t boxrc_out = { TW, y, wndsz.x - TW - TW, (TW &amp;lt;&amp;lt; 2) + 4 };
        rect_t boxrc_in = { boxrc_out.x + 1, boxrc_out.y + 1, boxrc_out.w - 2, boxrc_out.h - 2 };
        rect_t boxrc = { boxrc_in.x + (TW &amp;gt;&amp;gt; 1), boxrc_in.y + 1, boxrc_in.w - (TW &amp;gt;&amp;gt; 1), boxrc_in.h - 2};
        int maxl = boxrc.w / TW;
 
        rect_t rc = boxrc_in;
        int ret = (ms.x &amp;gt;= rc.x &amp;amp;&amp;amp; ms.y &amp;gt; rc.y &amp;amp;&amp;amp; ms.x &amp;lt;= rc.x + rc.w &amp;amp;&amp;amp; ms.y &amp;lt;= rc.y + rc.h) ? 1 : 0;
 
        display.get_surface().fill(rgba(255, 255, 66, 66), boxrc_out, blend.Alpha_rgba);
        display.get_surface().fill(ret ? (isnearest ? orange : rgba(255, 255, 66, 66)) : (isnearest ? rgba(255, 66, 66, 255) : rgba(255, 181, 181, 255)), boxrc_in, blend.Alpha_rgba);
 
        string ss;
        bool spc = false, end = false;
        int len = 0; bool isdbcs = false;
        for (char&amp;amp; i : str) {
            isdbcs = !isdbcs &amp;amp;&amp;amp; i &amp;lt; 0;
            if (isdbcs &amp;amp;&amp;amp; (!end &amp;amp;&amp;amp; len + 1 == maxl))
            { ss.push_back(&apos; &apos;); ++ len;    }
            else if (isdbcs &amp;amp;&amp;amp; (end &amp;amp;&amp;amp; len + 4 == maxl))
            { ss.push_back(&apos; &apos;); continue;  }
            if (len &amp;gt;= maxl) {
                if (end) {
                    for (int j = len; j &amp;amp;&amp;amp; j != len - 3; --j)
                        ss[unsigned(j)] = &apos;.&apos;;
                    break;
                }
                ss.push_back(&apos;\n&apos;);
                end = true; maxl &amp;lt;&amp;lt;= 1;
            }
            if (i == &apos; &apos; || i == &apos;\t&apos; || i == &apos;\n&apos; || i == &apos;\r&apos;) {
                if (!spc)
                    ss.push_back(&apos;`&apos;), spc = true, ++len;
                continue;
            }
            ss.push_back(i), spc = false, ++len;
        }
 
        font_t ft(&quot;宋体&quot;, { TW, TW &amp;lt;&amp;lt; 1 });
        surface_t s = ft.render(ss.c_str(), false, 0xff000000);
        display.get_surface().blit(s, {boxrc.x, boxrc.y}, 0, blend.Alpha_rgba);
 
        rect_t rc2 = { boxrc.x + boxrc.w - (TW * 3), boxrc.y + TW + 1, TW &amp;lt;&amp;lt; 1, TW &amp;lt;&amp;lt; 1 };
        int ret2 = (ms.x &amp;gt;= rc2.x &amp;amp;&amp;amp; ms.y &amp;gt; rc2.y &amp;amp;&amp;amp; ms.x &amp;lt;= rc2.x + rc2.w &amp;amp;&amp;amp; ms.y &amp;lt;= rc2.y + rc2.h) ? 2 : ret;
        font_t ft2(&quot;宋体&quot;, { TW, TW &amp;lt;&amp;lt; 1 });
        s = ft2.render(&quot;×&quot;, false, ret2 == 2 ? lightblue : (ret ? 0xff000000 : 0xff444444), rgba(255, 100, 66, 166));
        display.get_surface().blit(s, {rc2.x, rc2.y}, 0, blend.Alpha_rgba);
 
        rect_t rc3 = { boxrc.x + boxrc.w - (TW * 9), boxrc.y + TW + 1, TW * 5, TW &amp;lt;&amp;lt; 1 };
        int ret3 = (ms.x &amp;gt;= rc3.x &amp;amp;&amp;amp; ms.y &amp;gt; rc3.y &amp;amp;&amp;amp; ms.x &amp;lt;= rc3.x + rc3.w &amp;amp;&amp;amp; ms.y &amp;lt;= rc3.y + rc3.h) ? 3 : ret2;
        font_t ft3(&quot;宋体&quot;, { TW, TW &amp;lt;&amp;lt; 1 });
        s = ft3.render(&quot; Mod &quot;, false, ret3 == 3 ? lightblue : (ret ? 0xff000000 : 0xff444444), rgba(255, 100, 66, 166));
        display.get_surface().blit(s, { rc3.x, rc3.y }, 0, blend.Alpha_rgba);
        return ret3;
    }
 
    int print_title() {
        point2d_t ps = display.get_window_size();
        point2d_t ms = mouse.get_pos();
        display.get_surface().fill(rgba(255, 222, 222, 255), { 0, 0, ps.x, sys.sy - 2 });
 
        font_t ft(&quot;宋体&quot;, { TW - 2, (TW - 2) &amp;lt;&amp;lt; 1 }, true);
        surface_t s = ft.render(&quot;剪切板&quot;, false, 0xff000000);
        display.get_surface().blit(s, { TW + 1, TW * 2 / 3 }, 0, blend.Alpha_rgba);
 
        string st;
        stringstream ss;
        ss &amp;lt;&amp;lt; &apos;(&apos; &amp;lt;&amp;lt; sys._len &amp;lt;&amp;lt; &apos;/&apos; &amp;lt;&amp;lt; sys.MX &amp;lt;&amp;lt; &apos;)&apos;;
        ss &amp;gt;&amp;gt; st;
 
        font_t ft2(&quot;宋体&quot;, { TW - 2, (TW - 2) &amp;lt;&amp;lt; 1 });
        s = ft2.render(st.c_str(), false, 0xff777777);
        display.get_surface().blit(s, { TW + (TW * 6), TW * 2 / 3 }, 0, blend.Alpha_rgba);
 
        rect_t rc = { ps.x - TW - 1 - (TW &amp;lt;&amp;lt; 2), TW * 2 / 3, (TW - 1) &amp;lt;&amp;lt; 2, (TW - 1) &amp;lt;&amp;lt; 1 };
        font_t ft3(&quot;宋体&quot;, { TW - 1, (TW - 1) &amp;lt;&amp;lt; 1 }, true);
        int ret = (ms.x &amp;gt;= rc.x &amp;amp;&amp;amp; ms.y &amp;gt; rc.y &amp;amp;&amp;amp; ms.x &amp;lt;= rc.x + rc.w &amp;amp;&amp;amp; ms.y &amp;lt;= rc.y + rc.h) ? 1 : 0;
        s = ft3.render(&quot;清空&quot;, false, ret ? aliceblue : 0xff6994a2);
        display.get_surface().blit(s, {rc.x, rc.y}, 0, blend.Alpha_rgba);
 
        rect_t rc2 = { ps.x - TW - 1 - (TW &amp;lt;&amp;lt; 3) - TW, TW * 2 / 3, (TW - 1) &amp;lt;&amp;lt; 2, (TW - 1) &amp;lt;&amp;lt; 1};
        font_t ft4(&quot;宋体&quot;, { TW - 1, (TW - 1) &amp;lt;&amp;lt; 1 }, true);
        int ret2 = (ms.x &amp;gt;= rc2.x &amp;amp;&amp;amp; ms.y &amp;gt; rc2.y &amp;amp;&amp;amp; ms.x &amp;lt;= rc2.x + rc2.w &amp;amp;&amp;amp; ms.y &amp;lt;= rc2.y + rc2.h) ? 2 : ret;
        s = ft4.render(&quot;搜索&quot;, false, ret2 == 2 ? aliceblue : 0xff6994a2);
        display.get_surface().blit(s, {rc2.x, rc2.y}, 0, blend.Alpha_rgba);
 
        return ret2;
    }
 
    double algo_lwst(wstring&amp;amp; a, wstring b){
        size_t n = a.length(), m = b.length();
        vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; arr(n, vector&amp;lt;int&amp;gt;(m));
        auto fc = [](wchar_t c) {return c &amp;gt;= &apos;A&apos; &amp;amp;&amp;amp; c &amp;lt;= &apos;Z&apos; ? c - &apos;A&apos; + &apos;a&apos; : c; };
 
        for (size_t i = 0; i != n; ++ i) {
            if (fc(b[0]) == fc(a[i])) arr[i][0] = int(i);
            else if (i &amp;gt; 0) arr[i][0] = arr[i-1][0] + 1;
            else arr[i][0] = 1;
        }
        for (size_t j = 0; j != m; ++ j) {
            if (fc(a[0]) == fc(b[j])) arr[0][j] = int(j);
            else if (j &amp;gt; 0) arr[0][j] = arr[0][j-1] + 1;
            else arr[0][j] = 1;
        }
        for (size_t i = 1; i != n; ++i)
            for (size_t j = 1; j != m; ++j) {
                if (fc(a[i]) == fc(b[j])) arr[i][j] = min(arr[i-1][j]+1, min(arr[i][j-1]+1, arr[i-1][j-1]));
                else arr[i][j] = min(arr[i-1][j]+1, min(arr[i][j-1]+1, arr[i-1][j-1]+1));
            }
        double ret = pow(.5, double(arr[n - 1][m - 1]));
        for (size_t i = 0; i != n; ++ i) {
            if (fc(a[i]) == fc(b[0])) arr[i][0] = 1;
            else if (i != 0) arr[i][0] = arr[i-1][0];
            else arr[i][0] = 0;
        }
        for (size_t j = 0; j != m; ++ j) {
            if (fc(a[0]) == fc(b[j])) arr[0][j] = 1;
            else if (j) arr[0][j] = arr[0][j-1];
            else arr[0][j] = 0;
        }
        for (size_t i = 1; i != n; ++ i)
            for (size_t j = 1; j != m; ++ j) {
                if (fc(a[i]) == fc(b[j])) arr[i][j] = max(max(arr[i][j-1], arr[i-1][j]), arr[i-1][j-1]+1);
                else arr[i][j] = max(max(arr[i][j-1], arr[i-1][j]), arr[i-1][j-1]);
            }
        ret += double(arr[n - 1][m - 1]);
        return ret;
    }
 
    void paste(std::wstring &amp;amp;str) {
        size_t len = str.length(), i = 0, idx = 0;
        if (len == 0) return;
        std::vector&amp;lt;INPUT&amp;gt; in(len &amp;lt;&amp;lt; 1);
        ZeroMemory(&amp;amp;in[0], in.size() * sizeof(INPUT));
        while (i != len) {
            WORD ch = static_cast&amp;lt;WORD&amp;gt;(str[i++]);
            if (ch == 0x000A) {
                in[idx].type = INPUT_KEYBOARD;
                in[idx].ki.wVk = VK_RETURN;
                in[idx].ki.dwFlags = 0;
                ++idx;
                in[idx] = in[idx - 1];
                in[idx].ki.dwFlags |= KEYEVENTF_KEYUP;
                ++idx;
                continue;
            }
            in[idx].type = INPUT_KEYBOARD;
            in[idx].ki.wScan = ch;
            in[idx].ki.dwFlags = KEYEVENTF_UNICODE;
            ++idx;
            if ((ch &amp;lt; 0xD800) || (ch &amp;gt; 0xDFFF)) {
                in[idx] = in[idx - 1];
                in[idx].ki.dwFlags |= KEYEVENTF_KEYUP;
                ++idx;
            } else {
                in[idx].type = INPUT_KEYBOARD;
                in[idx].ki.wScan = (WORD) str[i++];
                in[idx].ki.dwFlags = KEYEVENTF_UNICODE;
                ++idx;
                in[idx] = in[idx-2];
                in[idx].ki.dwFlags |= KEYEVENTF_KEYUP;
                ++idx;
                in[idx] = in[idx-2];
                in[idx].ki.dwFlags |= KEYEVENTF_KEYUP;
                ++idx;
            }
        }
        SendInput(UINT(in.size()), &amp;amp;in[0], sizeof(INPUT));
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;另外，此程序中用到的 inputbox.h 代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// inputbox.h
# include &quot;mclib/src/mclib.h&quot;
# pragma warning(disable: 5039)
# define WIN32_LEAN_AND_MEAN
# include &amp;lt;windows.h&amp;gt;
# include &amp;lt;windowsx.h&amp;gt;
# include &amp;lt;tchar.h&amp;gt;
# undef WIN32_LEAN_AND_MEAN
 
int WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
HINSTANCE _hInstance;
char* _lpWndMsg, * _lpDefValue, * _lpHelpFile;
TCHAR* _lpWndTitle;
int _nHelpIndex;
HWND _hParent, _hDesktop, _hEdit, _hBtnOk, _hBtnCancel, _hBtnHelp, _hMsgText;
RECT _st_rcDesktop, _st_rcWnd;
HFONT _hWndFont;
char _lpwfn[] = &quot;Arial&quot;;
char *_lpWndFontName = (char*)_lpwfn;
char* _szBuffer = 0; LRESULT lgt = 0;
UINT _nMaxLine = 2048-1, _nEditStyle = WS_BORDER | WS_CHILD | WS_VISIBLE | ES_AUTOVSCROLL | ES_MULTILINE | SS_LEFT; /* ES_AUTOHSCROLL */
 
int WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    HDC hWndDc;
    WORD uBtnID;
    LRESULT lpm = 12;
    switch (uMsg) {
    case WM_DESTROY:
        if (_hWndFont) DeleteObject(_hWndFont);
        PostQuitMessage(0);
        break;
    case WM_CREATE:
        _hMsgText = CreateWindowExA(0, &quot;Static&quot;, _lpWndMsg, WS_CHILD | WS_VISIBLE,
            5, 5, 275, 70, hWnd, (HMENU)1000, _hInstance, 0);
        _hBtnOk = CreateWindowExA(0, (&quot;Button&quot;), (&quot;确定(&amp;amp;K)&quot;),
            WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 285, 5, 65,
            20, hWnd, (HMENU)IDOK, _hInstance, 0);
        _hBtnCancel = CreateWindowExA(0, (&quot;Button&quot;), (&quot;取消(&amp;amp;C)&quot;), WS_CHILD | WS_VISIBLE,
            285, 30, 65, 20, hWnd, (HMENU)IDCANCEL, _hInstance, 0);
        _hBtnHelp = CreateWindowExA(0, (&quot;Button&quot;), (&quot;帮助(&amp;amp;H)&quot;), WS_CHILD | WS_VISIBLE,
            285, 55, 65, 20, hWnd, (HMENU)IDHELP, _hInstance, 0);
        _hEdit = CreateWindowExA(WS_EX_CLIENTEDGE, (&quot;Edit&quot;), _lpDefValue, _nEditStyle,
            5, 5, 275, 85, hWnd, (HMENU)2000, _hInstance, 0);
        /* SendMessage(_hEdit, EM_SETLIMITTEXT, _nMaxLine, 0); */
        SendMessage(_hEdit, EM_SETTABSTOPS, 1, LPARAM(&amp;amp;lpm));
 
        _hWndFont = CreateFontA(12, 6, 0, 0, 12, 0, 0, 0, GB2312_CHARSET,
            OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
            DEFAULT_PITCH, _lpWndFontName);
        if (!lstrlenA(_lpHelpFile))
            ShowWindow(_hBtnHelp, SW_HIDE);
        hWndDc = GetDC(hWnd);
        SelectObject(hWndDc, _hWndFont);
        ReleaseDC(hWnd, hWndDc);
        SendDlgItemMessageA(hWnd, 1000, WM_SETFONT, WPARAM(_hWndFont), 0);
        SendDlgItemMessageA(hWnd, 2000, WM_SETFONT, WPARAM(_hWndFont), 0);
        SendDlgItemMessageA(hWnd, IDOK, WM_SETFONT, WPARAM(_hWndFont), 0);
        SendDlgItemMessageA(hWnd, IDCANCEL, WM_SETFONT, WPARAM(_hWndFont), 0);
        SendDlgItemMessageA(hWnd, IDHELP, WM_SETFONT, WPARAM(_hWndFont), 0);
        break;
    case WM_SIZE: /*  365,130-&amp;gt;285,30 */
        ::SetWindowPos(_hBtnOk, 0, GET_X_LPARAM(lParam) - 70, 5, 65, 20, SWP_FRAMECHANGED);
        ::SetWindowPos(_hBtnCancel, 0, GET_X_LPARAM(lParam) - 70, 30, 65, 20, SWP_FRAMECHANGED);
        ::SetWindowPos(_hBtnHelp, 0, GET_X_LPARAM(lParam) - 70, 55, 65, 20, SWP_FRAMECHANGED);
        ::SetWindowPos(_hMsgText, 0, 5, 5, 1,1, SWP_FRAMECHANGED);
        ::SetWindowPos(_hEdit, 0, 5, 5, GET_X_LPARAM(lParam) - 85, GET_Y_LPARAM(lParam)-10, 0);
        break;
    case WM_KEYDOWN:
        if (wParam == VK_RETURN)
            SendMessage(hWnd, WM_COMMAND, IDOK, 0);
        break;
    case WM_SETFOCUS:
        SetFocus(_hEdit);
        break;
    case WM_COMMAND:
        uBtnID = LOWORD(wParam);
        switch (uBtnID) {
        case IDOK:
            lpm = SendMessage(_hEdit, WM_GETTEXTLENGTH, 0, 0) + 10;
            if (lgt &amp;lt; lpm) {
                lgt = lpm;
                delete[] _szBuffer;
                _szBuffer = new(std::nothrow) char[size_t(lgt)];
            }
            if (_szBuffer)
                GetDlgItemTextA(hWnd, 2000, _szBuffer, int(lgt));
 
            DestroyWindow(hWnd);
            break;
        case IDCANCEL:
            DestroyWindow(hWnd);
            break;
        case IDHELP:
            WinHelpA(hWnd, _lpHelpFile, HELP_INDEX, ULONG_PTR(_nHelpIndex));
            break;
        };
        break;
    default:
        return int(DefWindowProc(hWnd, uMsg, wParam, lParam));
    }
    return true;
}
char* input_box(char* lpWndMsg = NULL, TCHAR* lpWndTitle = NULL,
    char* lpDefValue = NULL,
    int xPos = 0, int yPos = 0, int fIsType = 0,
    char* lpHelpFile = NULL, int nHelpIndex = 0)
{
    if (!_szBuffer) {
        _szBuffer = new(nothrow) char[1];
        lgt = 1;
    }
    if (_szBuffer)
        _szBuffer[0] = 0;
    _hParent = mcl::display.get_wm_info()[&quot;window&quot;];
    _lpWndMsg = lpWndMsg;
    _lpWndTitle = lpWndTitle;
    _lpDefValue = lpDefValue;
    _lpHelpFile = lpHelpFile;
    _nHelpIndex = nHelpIndex;
    if (fIsType) _nEditStyle |= fIsType;
    _hInstance = GetModuleHandle(NULL);
    _hDesktop = GetDesktopWindow();
    GetWindowRect(_hDesktop, &amp;amp;_st_rcDesktop);
    if (!xPos) xPos = (_st_rcDesktop.right - 365) / 2;
    if (!yPos) yPos = (_st_rcDesktop.bottom - 130) / 2;
    WNDCLASSEX st_WndClass;
    HWND hWnd;
    RtlZeroMemory(&amp;amp;st_WndClass, sizeof(st_WndClass));
    st_WndClass.cbSize = sizeof(st_WndClass);
    st_WndClass.hInstance = _hInstance;
    st_WndClass.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;
    st_WndClass.hCursor = LoadCursor(0, IDC_ARROW);
    st_WndClass.hIcon = LoadIcon(0, IDI_APPLICATION);
    st_WndClass.hIconSm = st_WndClass.hIcon;
 
# pragma warning(push)
# pragma warning(disable: 4191)
    st_WndClass.lpfnWndProc = reinterpret_cast&amp;lt;WNDPROC&amp;gt;(&amp;amp;WndProc);
# pragma warning(pop)
    st_WndClass.lpszClassName = _T(&quot;InputBox_Class&quot;);
    st_WndClass.style = CS_HREDRAW | CS_VREDRAW;
    RegisterClassEx(&amp;amp;st_WndClass);
    hWnd = CreateWindowEx(0, _T(&quot;InputBox_Class&quot;), _lpWndTitle,
        WS_DLGFRAME | WS_SYSMENU | WS_VISIBLE | WS_THICKFRAME, xPos, yPos,
        365, 130, _hParent, 0, _hInstance, 0);
    MSG st_Msg;
    if (!hWnd) return 0;
    ShowWindow(hWnd, SW_SHOW);
    UpdateWindow(hWnd);
    while (GetMessage(&amp;amp;st_Msg, 0, 0, 0)) {
        if (st_Msg.message == WM_KEYDOWN)
            if ((st_Msg.wParam == VK_RETURN) &amp;amp;&amp;amp; (::GetAsyncKeyState(VK_CONTROL) &amp;amp; 0x8000))
                SendMessage(hWnd, st_Msg.message, st_Msg.wParam, LPARAM(st_Msg.wParam));
        TranslateMessage(&amp;amp;st_Msg);
        DispatchMessage(&amp;amp;st_Msg);
    }
    return _szBuffer;
}
 
int MessageBoxCentered(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
    if (!hWnd) hWnd = display.get_wm_info()[&quot;window&quot;];
    static HHOOK hHookCBT{};
    hHookCBT = SetWindowsHookEx(WH_CBT,
        [](int nCode, WPARAM wParam, LPARAM lParam) -&amp;gt; LRESULT {
            if (nCode == HCBT_CREATEWND &amp;amp;&amp;amp; (LPCBT_CREATEWND(lParam))-&amp;gt;lpcs-&amp;gt;lpszClass == reinterpret_cast&amp;lt;LPTSTR&amp;gt;(ATOM(32770))) {
                    RECT rcParent{};
                    GetWindowRect(((LPCBT_CREATEWND)lParam)-&amp;gt;lpcs-&amp;gt;hwndParent, &amp;amp;rcParent);
                    ((LPCBT_CREATEWND)lParam)-&amp;gt;lpcs-&amp;gt;x = rcParent.left + ((rcParent.right - rcParent.left) - ((LPCBT_CREATEWND)lParam)-&amp;gt;lpcs-&amp;gt;cx) / 2;
                    ((LPCBT_CREATEWND)lParam)-&amp;gt;lpcs-&amp;gt;y = rcParent.top + ((rcParent.bottom - rcParent.top) - ((LPCBT_CREATEWND)lParam)-&amp;gt;lpcs-&amp;gt;cy) / 2;
            }
            return CallNextHookEx(hHookCBT, nCode, wParam, lParam);
        }, 0, GetCurrentThreadId()
    );
    int iRet = MessageBox(hWnd, lpText, lpCaption, uType);
    UnhookWindowsHookEx(hHookCBT);
    return iRet;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>如何使本地项目在 Github/Gitee 上同步并使用 GPG 验证</title><link>https://iamyukino.cn/blog/posts/upload-to-git-remotes-with-gpg/</link><guid isPermaLink="true">https://iamyukino.cn/blog/posts/upload-to-git-remotes-with-gpg/</guid><description>如何配置 GPG 密钥？如何一次提交就能同时上传 Github 和 Gitee？雨雪以萌新视角讲述了 Git/GPG 的配置流程，并配有相关图片。</description><pubDate>Fri, 01 Apr 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;第一步：首先你得有一个 github 仓库&lt;/h2&gt;
&lt;p&gt;首先，进入 &lt;a href=&quot;https://github.com/&quot;&gt;Github 官网&lt;/a&gt;，新建仓库，点击 + ，如图：&lt;/p&gt;
&lt;p&gt;如果访问 Github 失败，可以尝试打开 cmd 并输入 &lt;code&gt;ipconfig/flushdns&lt;/code&gt; 刷新 DNS 缓存。若长时间无法加载，可尝试使用 VPN 加速。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://iamyukino.cn/blog/_astro/p1.BGNCd_uR_Z1Y59Oh.webp&quot; alt=&quot;New Repository&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这里三个文件可以都选上，&lt;code&gt;Create repository&lt;/code&gt; 即可。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://iamyukino.cn/blog/_astro/p2.CrWqkjoZ_p1Slf.webp&quot; alt=&quot;Create Repository&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第二步：将 Github 仓库导入到 Gitee&lt;/h2&gt;
&lt;p&gt;同样地，进入 &lt;a href=&quot;https://gitee.com&quot;&gt;Gitee 官网&lt;/a&gt;，点击 &lt;code&gt;+&lt;/code&gt;，如图：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://iamyukino.cn/blog/_astro/p3.Cb1r8vyr_XqTxc.webp&quot; alt=&quot;Import Repository&quot; /&gt;&lt;/p&gt;
&lt;p&gt;同意链接 Github 账号后，点击导入 Github 仓库，选择刚才创建的仓库，点击导入即可。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://iamyukino.cn/blog/_astro/p4.Cy_ylPrJ_ZbEkNB.webp&quot; alt=&quot;Import to Gitee&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第三步：下载 git&lt;/h2&gt;
&lt;p&gt;下载 git 并安装。可前往 &lt;a href=&quot;https://git-scm.com/downloads&quot;&gt;Git 官网下载地址&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;准备一个空的文件夹并打开。右键，点击 &lt;code&gt;Git Bash Here&lt;/code&gt; 打开 git。win11 点 “显示更多选项”。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://iamyukino.cn/blog/_astro/p5.D_nD5QWf_CJ4N6.webp&quot; alt=&quot;Git Bash Here&quot; /&gt;&lt;/p&gt;
&lt;p&gt;输入以下代码。这里的邮箱请换成你注册 github 使用的邮箱。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git config --global user.name 你的名字
git config --global user.email 你的邮箱地址
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://iamyukino.cn/blog/_astro/p6.B6b8_eiV_1GumIf.webp&quot; alt=&quot;Git Config&quot; /&gt;&lt;/p&gt;
&lt;p&gt;复制 Github 中打开刚刚创建的仓库的链接，在 git 输入以下代码。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone https://仓库链接.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;检查本地，发现 github 上的内容已经下载到本地了。&lt;/p&gt;
&lt;h2&gt;第四步：创建 GPG 密钥&lt;/h2&gt;
&lt;p&gt;首先检查 GPG 版本。如图（划线部分）：&lt;/p&gt;
&lt;p&gt;注：新版本 Git Bash 的发行版本已内置可用的 gpg.exe。若提示无可用，建议重新下载最新版 git。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://iamyukino.cn/blog/_astro/p7.D_XwDwum_1Bc3YU.webp&quot; alt=&quot;Check GPG Version&quot; /&gt;&lt;/p&gt;
&lt;p&gt;如果 GPG 版本在 &lt;code&gt;2.1.17&lt;/code&gt; 以上，就用下面的命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gpg --full-generate-key
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;否则用下面的命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gpg --default-new-key-algo rsa4096 --gen-key
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;根据提示创建。红色方框指出的就是密钥 id。请记住这个密钥 id。如图：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://iamyukino.cn/blog/_astro/p8.BQWCAOby_Zi5vsJ.webp&quot; alt=&quot;GPG Full Generate Key&quot; /&gt;&lt;/p&gt;
&lt;p&gt;其中，输出结果末尾是生成的 GPG 密钥信息。后续可通过 &lt;code&gt;gpg --list-keys&lt;/code&gt; 查看。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;pub 公钥特征：包括密钥的加密算法、长度、生成时间、用途、过期时间和密钥 ID。&lt;/li&gt;
&lt;li&gt;uid 个人配置资料。&lt;/li&gt;
&lt;li&gt;ssb 子密钥特征：格式同公钥。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;然后输入以下命令。红框中的密钥 id 改为刚才的密钥 id。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gpg --armor --export 密钥id
git config --global user.signingkey 密钥id
git config --global commit.gpgsign true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://iamyukino.cn/blog/_astro/p9.DEP6-5j__1FUnH.webp&quot; alt=&quot;Git GPG&quot; /&gt;&lt;/p&gt;
&lt;p&gt;将从 &lt;code&gt;-----BEGIN PGP PUBLIC KEY BLOCK-----&lt;/code&gt; 到 &lt;code&gt;-----END PGP PUBLIC KEY BLOCK-----&lt;/code&gt; 中的内容复制到剪切板。&lt;/p&gt;
&lt;h2&gt;第五步：上传 GPG 密钥&lt;/h2&gt;
&lt;p&gt;回到 &lt;a href=&quot;https://github.com/&quot;&gt;Github&lt;/a&gt; 界面。右上角头像点击 &lt;code&gt;Settings&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://iamyukino.cn/blog/_astro/p10.B5pr_Gft_ZoUOsN.webp&quot; alt=&quot;Github Settings&quot; /&gt;&lt;/p&gt;
&lt;p&gt;点击 &lt;code&gt;SSH and GPG keys&lt;/code&gt; -&amp;gt; &lt;code&gt;New GPG Key&lt;/code&gt;。将刚才复制到剪切板的内容黏贴入框中。点击 &lt;code&gt;Add GPG key&lt;/code&gt;，输入密码，可看到 GPG key 已添加至 Github 界面。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://iamyukino.cn/blog/_astro/p11.D_5xhPDe_ZwHyUD.webp&quot; alt=&quot;SSH and GPG Keys&quot; /&gt;&lt;/p&gt;
&lt;p&gt;前往 &lt;a href=&quot;https://gitee.com/&quot;&gt;Gitee&lt;/a&gt; 界面。右上角点击 设置。在左边的安全设置中找到 &lt;code&gt;GPG 公钥&lt;/code&gt; 一栏，将剪切板内容黏贴入框中，点击确定，输入密码，可看到 GPG key 已添加至 Gitee 界面。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://iamyukino.cn/blog/_astro/p12.INxETGuS_1gbg6n.webp&quot; alt=&quot;Add GPG Key&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第六步：上传仓库&lt;/h2&gt;
&lt;p&gt;本地打开刚刚克隆的库，找到 &lt;code&gt;.git&lt;/code&gt; 文件夹点开。（若找不到可在文件资源管理器上方设置：显示隐藏的文件夹）
双击 &lt;code&gt;config&lt;/code&gt; 文件，以 txt 文本文档格式打开。复制红色框内的这一行，并另起一行黏贴，将此行中的链接改为 gitee 中仓库的链接。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://iamyukino.cn/blog/_astro/p13.yHjg_cIZ_Z2wTNBr.webp&quot; alt=&quot;Add Remote Origin&quot; /&gt;&lt;/p&gt;
&lt;p&gt;回到项目的目录（就是刚刚克隆到本地的项目的文件夹），将你的项目黏贴至此处。
右键，点击 &lt;code&gt;Git Bash Here&lt;/code&gt; 打开 &lt;code&gt;git&lt;/code&gt;（Win11 点 “显示更多选项”）
输入以下代码。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git add .
git commit -S&apos;这里输入密钥id&apos; -m&apos;这里可随意输入内容，也可随意换行，是提交的相关信息备注。&apos;
git push
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://iamyukino.cn/blog/_astro/p14.Cw4Z2ns9_3exe2.webp&quot; alt=&quot;First Commit&quot; /&gt;&lt;/p&gt;
&lt;p&gt;前往 Gitee 和 Github 查看，发现项目已经成功上传了，并标有已验证的字样。至此，项目的初次提交工作已全部完成。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://iamyukino.cn/blog/_astro/p15.B_mm9Yed_1QxNXd.webp&quot; alt=&quot;Commit Successful&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;结尾：二次提交&lt;/h2&gt;
&lt;p&gt;在项目文件夹中右键打开 git，输入以下内容。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git add .
git commit -m&apos;任意内容&apos;
git push
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;发现 Github 和 Gitee 均同步更新了。是不是很酷？&lt;/p&gt;
</content:encoded></item><item><title>Python 元组在 C++ 中的简易实现</title><link>https://iamyukino.cn/blog/posts/impl-of-py-tuples-in-cpp/</link><guid isPermaLink="true">https://iamyukino.cn/blog/posts/impl-of-py-tuples-in-cpp/</guid><description>运行时期C++如何按下标索引元组？如何遍历输出元组？雨雪在C++中简单实现了Python元组的一些特性，并给出了详细代码。</description><pubDate>Fri, 25 Mar 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;:::TIP
不做&lt;a href=&quot;../language-lawyer/&quot;&gt;半桶水的 C++ “语言律师”&lt;/a&gt;（language lawyers）！&lt;br /&gt;
本文仅供抛砖引玉，请勿深究。
:::&lt;/p&gt;
&lt;h2&gt;元组&lt;/h2&gt;
&lt;p&gt;Python 的元组与列表类似，区别在于其元素不可修改。元组的最大特点是可存放任意类型，所以我们可使用其将不相关的变量绑定为一个整体。C++ 作为编译型语言，往往通过模板（编译期），或通过类似于函数闭包的方法（运行期，如 &lt;code&gt;std::any&lt;/code&gt;），实现不定类型元组。C++ 标准库中的元组类型，是通过模板多态实现的。&lt;/p&gt;
&lt;p&gt;在 Python 中，元组创建很简单，只需要在括号中添加元素，并使用逗号隔开即可。
就像这样：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;t1 = (&apos;Hello&apos;, 111)  
t2 = &quot;How&quot;, &quot;are&quot;, &quot;you&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而 C++ 也有现成的元组类型 &lt;code&gt;std::tuple&lt;/code&gt;，在 &lt;code&gt;&amp;lt;tuple&amp;gt;&lt;/code&gt; 中定义。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;auto t1 = std::make_tuple (&quot;My&quot;, 22);
auto t2 = std::make_tuple (&quot;Name&quot;, &quot;is&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;元组与数组类似，下标索引从 0 开始。&lt;/p&gt;
&lt;h2&gt;访问元组&lt;/h2&gt;
&lt;p&gt;我们知道，Python 中元组可以使用下标索引来访问元组中的值。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;t3 = (&apos;Yukino&apos;, &apos;Amamiya&apos;)
print (&apos;First Name&apos;, t3[-1])
print (&apos;Last Name&apos;, t3[0])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在编译期，C++ 也可通过 &lt;code&gt;std::get&amp;lt;N&amp;gt;()&lt;/code&gt; 来获取指定位置的变量。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;auto t3 = std::make_tuple (&apos;You&apos;, &apos;can&apos;, &apos;call me&apos;);
std::cout &amp;lt;&amp;lt; std::get&amp;lt;0&amp;gt;(t3) &amp;lt;&amp;lt; std::endl;
std::cout &amp;lt;&amp;lt; std::get&amp;lt;1&amp;gt;(t3) &amp;lt;&amp;lt; std::endl;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而在运行时期，C++ 需要通过递归的方式来逐一比较编译器和运行期常量的值，从而获取指定下标的元素。而递归会导致找到时所处位置在函数调用栈的深处，很难返回，故在这里我通过赋值的方式返回。&lt;/p&gt;
&lt;p&gt;注意代码中赋值时使用了模板类型萃取条件，从而避免元组中有不是想要的下标元素，类型不匹配导致编译错误。&lt;/p&gt;
&lt;p&gt;就像这样：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* Runtime subscript access tuples */
constexpr int /* Recursive termination condition */
mcl_tuple_visitor (size_t, std::tuple&amp;lt;&amp;gt; const&amp;amp;, void*) { return 0; }

template &amp;lt;typename lhs_t, typename rhs_t&amp;gt;
constexpr typename std::enable_if&amp;lt;!std::is_convertible&amp;lt;rhs_t, lhs_t&amp;gt;::value, void&amp;gt;::type
mcl_copy (lhs_t* , rhs_t* ) /* The specified subscript element type cannot be */
{ throw std::bad_cast (); } /* implicitly converted to the target type */

template &amp;lt;typename lhs_t, typename rhs_t&amp;gt;
constexpr typename std::enable_if&amp;lt;std::is_convertible&amp;lt;rhs_t, lhs_t&amp;gt;::value, void&amp;gt;::type
mcl_copy (lhs_t* lhs, rhs_t* rhs) noexcept
{ const_cast&amp;lt;typename std::remove_const&amp;lt;decltype(*lhs)&amp;gt;::type&amp;gt;(*lhs) = *rhs; }

template &amp;lt;typename visit_t, typename Tv, typename... Ts&amp;gt; void 
mcl_tuple_visitor (size_t index, std::tuple&amp;lt;Tv, Ts...&amp;gt; const&amp;amp; t, visit_t* ptr) {
    if (index &amp;gt;= (1 + sizeof...(Ts))) 
        throw std::invalid_argument (&quot;Bad Index&quot;);
    else if (index &amp;gt; 0)
        mcl_tuple_visitor (index - 1, reinterpret_cast&amp;lt;std::tuple&amp;lt;Ts...&amp;gt; const&amp;amp;&amp;gt;(t), ptr);
    else mcl_copy (ptr, &amp;amp;std::get&amp;lt;0&amp;gt;(t));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用 &lt;code&gt;operator&amp;lt;&amp;lt;&lt;/code&gt; 输出时，由于模板不指定目标类型，故需要进行特殊化处理。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* Outputs specified tuple elements at run time */
struct mcl_do_is_printable {
    template&amp;lt;typename lhs_t, typename rhs_t, 
        typename = decltype(*static_cast&amp;lt;lhs_t*&amp;gt;(0) &amp;lt;&amp;lt; *static_cast&amp;lt;rhs_t*&amp;gt;(0))&amp;gt;
        static std::integral_constant&amp;lt;bool, true&amp;gt; test(int); /* only declaration */
    template&amp;lt;typename, typename&amp;gt;
        static std::integral_constant&amp;lt;bool, false&amp;gt; test(...);
};

template&amp;lt;typename lhs_t, typename rhs_t&amp;gt;
struct mcl_is_printable
: public mcl_do_is_printable {
    typedef decltype(test&amp;lt;lhs_t, rhs_t&amp;gt;(0)) type;
}; /* Judge whether the target type can be output */

template &amp;lt;typename lhs_t, typename rhs_t&amp;gt;
constexpr typename std::enable_if&amp;lt;mcl_is_printable&amp;lt;lhs_t, rhs_t&amp;gt;::type::value, void&amp;gt;::type
mcl_oss_print (lhs_t* lhs, rhs_t* rhs) noexcept
{ *lhs &amp;lt;&amp;lt; *rhs; }

template &amp;lt;typename lhs_t, typename rhs_t&amp;gt;
constexpr typename std::enable_if&amp;lt;!mcl_is_printable&amp;lt;lhs_t, rhs_t&amp;gt;::type::value, void&amp;gt;::type
mcl_oss_print (lhs_t* , rhs_t* ) 
{ throw std::ios_base::failure (&quot;The specified type does not overload operator&amp;lt;&amp;lt; . &quot;
    &quot;(with type = &quot; + std::string (typeid(rhs_t).name()) + &quot;)&quot;); }

constexpr int
mcl_tuple_printer (size_t, std::tuple&amp;lt;&amp;gt; const&amp;amp;, void*) noexcept{ return 0; }

template &amp;lt;typename os_t, typename Tv, typename... Ts&amp;gt; void 
mcl_tuple_printer (size_t index, std::tuple&amp;lt;Tv, Ts...&amp;gt; const&amp;amp; t, os_t* oss) {
    if (index &amp;gt;= (1 + sizeof...(Ts)))
        throw std::invalid_argument (&quot;Bad index&quot;);
    else if (index &amp;gt; 0)
        mcl_tuple_printer (index - 1, reinterpret_cast&amp;lt;std::tuple&amp;lt;Ts...&amp;gt; const&amp;amp;&amp;gt;(t), oss);
    else mcl_oss_print (oss, &amp;amp;std::get&amp;lt;0&amp;gt;(t));
}
    /* Use the characteristics of compile time extension to obtain the elements with
        specified subscripts through recursion at run time */
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;元组大小比较&lt;/h2&gt;
&lt;p&gt;Python 中，我们可以直接比较两个元组的大小，其规则是：&lt;/p&gt;
&lt;p&gt;如果比较的元素是同类型的，则比较其值，返回结果。&lt;br /&gt;
如果两个元素不是同一种类型,则检查它们是否是数字。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果是数字，执行必要的数字强制类型转换，然后比较。&lt;/li&gt;
&lt;li&gt;如果有一方的元素是数字，则另一方的元素“大”（数字是“最小的”）&lt;/li&gt;
&lt;li&gt;否则，通过类型名字的字母顺序进行比较。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果有一个列表首先到达末尾,则另一个长一点的列表”大”。&lt;br /&gt;
如果我们用尽了两个列表的元素而且所 有元素都是相等的,那么结果就是个平局,就是说返回一个 0。&lt;/p&gt;
&lt;p&gt;而对于 C++ 中的 &lt;code&gt;std::tuple&lt;/code&gt;，仅支持类型完全相同的两个元组之间的比较。为实现 python 中元组的比较效果，我们可以通过如下方式实现：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* Compare between tuples of defferent types */
template &amp;lt;typename... lhs_t, typename... rhs_t&amp;gt;
constexpr typename std::enable_if&amp;lt;!sizeof...(lhs_t) &amp;amp;&amp;amp; sizeof...(rhs_t), int&amp;gt;::type
cmp (std::tuple&amp;lt;lhs_t...&amp;gt; const&amp;amp; , std::tuple&amp;lt;rhs_t...&amp;gt; const&amp;amp; ) noexcept
{ return -1; } /* lhs reaches the end first, then rhs is larger */

template &amp;lt;typename... lhs_t, typename... rhs_t&amp;gt;
constexpr typename std::enable_if&amp;lt;sizeof...(lhs_t) &amp;amp;&amp;amp; !sizeof...(rhs_t), int&amp;gt;::type
cmp (std::tuple&amp;lt;lhs_t...&amp;gt; const&amp;amp; , std::tuple&amp;lt;rhs_t...&amp;gt; const&amp;amp; ) noexcept
{ return 1; } /* rhs reaches the end first, then lhs is larger */

template &amp;lt;typename... lhs_t, typename... rhs_t&amp;gt;
constexpr typename std::enable_if&amp;lt;!sizeof...(lhs_t) &amp;amp;&amp;amp; !sizeof...(rhs_t), int&amp;gt;::type
cmp (std::tuple&amp;lt;lhs_t...&amp;gt; const&amp;amp; , std::tuple&amp;lt;rhs_t...&amp;gt; const&amp;amp; ) noexcept
{ return 0; } /* lhs and rhs reaches the end at the same time, this means two tuples
                    are exactly equal. */
template &amp;lt;typename lhsf, typename rhsf, typename... lhs_t, typename... rhs_t&amp;gt;
constexpr typename std::enable_if&amp;lt;
    !(std::is_same&amp;lt;lhsf, rhsf&amp;gt;::value
        || (std::is_arithmetic&amp;lt;lhsf&amp;gt;::value &amp;amp;&amp;amp; std::is_arithmetic&amp;lt;rhsf&amp;gt;::value)
    ), int&amp;gt;::type
cmp (std::tuple&amp;lt;lhsf, lhs_t...&amp;gt; const&amp;amp;, std::tuple&amp;lt;rhsf, rhs_t...&amp;gt; const&amp;amp;) noexcept {
    return std::is_arithmetic&amp;lt;lhsf&amp;gt;::value ? -1
        : ( std::is_arithmetic&amp;lt;rhsf&amp;gt;::value ? 1 : (sizeof (lhsf) &amp;gt; sizeof (rhsf)) );
} /* The end is not reached and the first element type is different. Compare the type,
        and the number type is smaller. Otherwise, press sizeof for comparison. */
template &amp;lt;typename lhsf, typename rhsf, typename... lhs_t, typename... rhs_t&amp;gt;
constexpr typename std::enable_if&amp;lt;
    (std::is_same&amp;lt;lhsf, rhsf&amp;gt;::value
        || (std::is_arithmetic&amp;lt;lhsf&amp;gt;::value &amp;amp;&amp;amp; std::is_arithmetic&amp;lt;rhsf&amp;gt;::value)
    ), int&amp;gt;::type
cmp (std::tuple&amp;lt;lhsf, lhs_t...&amp;gt; const&amp;amp; lhs, std::tuple&amp;lt;rhsf, rhs_t...&amp;gt; const&amp;amp; rhs) noexcept {
    return std::get&amp;lt;0&amp;gt;(lhs) == std::get&amp;lt;0&amp;gt;(rhs) ? 
        cmp (reinterpret_cast&amp;lt;std::tuple&amp;lt;lhs_t...&amp;gt; const&amp;amp;&amp;gt;(lhs),
                reinterpret_cast&amp;lt;std::tuple&amp;lt;rhs_t...&amp;gt; const&amp;amp;&amp;gt;(rhs))
        : (std::get&amp;lt;0&amp;gt;(lhs) &amp;gt; std::get&amp;lt;0&amp;gt;(rhs)) - (std::get&amp;lt;0&amp;gt;(lhs) &amp;lt; std::get&amp;lt;0&amp;gt;(rhs));
} /* If the end is not reached and the first element type is the same, compare whether the
        first element of two tuples is equal. If it is not equal, the comparison result is
        returned, and if it is equal, the next element is compared recursively. */
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;元组长度&lt;/h2&gt;
&lt;p&gt;Python 中，我们可以像这样获取元组长度，或访问元组中的指定位置的元素，如下所示：&lt;/p&gt;
&lt;p&gt;元组：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;N = (&apos;Yukinochan&apos;, &apos;雨雪酱&apos;, &apos;みぞれちゃん&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Python 表达式&lt;/th&gt;
&lt;th&gt;结果&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;len(N)&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;获取元组长度&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N[2]&lt;/td&gt;
&lt;td&gt;‘みぞれちゃん’&lt;/td&gt;
&lt;td&gt;读取第三个元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N[-2]&lt;/td&gt;
&lt;td&gt;‘雨雪酱’&lt;/td&gt;
&lt;td&gt;反向读取，读取倒数第二个元素&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;对于 C++，获取元组长度可使用 &lt;code&gt;std::tuple_size&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;auto N = std::make_tuple (&apos;雨宮雪乃&apos;, &apos;雨雪ちゃん&apos;);
std::cout &amp;lt;&amp;lt; &quot;len = &quot; &amp;lt;&amp;lt; std::tuple_size&amp;lt;decltype(N)&amp;gt;::value;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;简易对其进行一个封装：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template &amp;lt;typename... T&amp;gt; 
constexpr size_t len (std::tuple&amp;lt;T...&amp;gt; const&amp;amp;) noexcept 
{ return sizeof... (T); }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;元组索引&lt;/h2&gt;
&lt;p&gt;而运行时期按下标进行访问，我们上面已经实现了核心代码，现在只需要创建一个继承自 &lt;code&gt;std::tuple&lt;/code&gt; 的类，姑且取名叫做 &lt;code&gt;pytuple&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template &amp;lt;typename... T&amp;gt;
class pytuple
: public std::tuple&amp;lt;T...&amp;gt; {
public:
    constexpr pytuple () = default;
    constexpr pytuple (const pytuple&amp;lt;T...&amp;gt;&amp;amp;) = default;
    constexpr pytuple (pytuple&amp;amp;&amp;amp;) = default;
    pytuple&amp;lt;T...&amp;gt;&amp;amp; operator= (pytuple&amp;lt;T...&amp;gt; const&amp;amp;) = default;
    pytuple&amp;lt;T...&amp;gt;&amp;amp; operator= (pytuple&amp;lt;T...&amp;gt;&amp;amp;&amp;amp;) = default;

    constexpr pytuple (std::tuple&amp;lt;T...&amp;gt; const&amp;amp; tp)
    : std::tuple&amp;lt;T...&amp;gt; (tp) { }

    constexpr pytuple (T&amp;amp;&amp;amp;... parms)
    : std::tuple&amp;lt;T...&amp;gt; (std::make_tuple (std::forward&amp;lt;T&amp;gt;(parms)...)) { }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;定义函数 &lt;code&gt;maktuple&lt;/code&gt;，以便于创建元组对象。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template &amp;lt;typename... Ts&amp;gt;
constexpr pytuple&amp;lt;typename std::decay&amp;lt;typename std::remove_reference&amp;lt;Ts&amp;gt;::type&amp;gt;::type...&amp;gt; 
maktuple (Ts&amp;amp;&amp;amp;... agv) noexcept {
    return std::make_tuple (std::forward&amp;lt;Ts&amp;gt;(agv)...);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;并重载 &lt;code&gt;pytuple::operator[]&lt;/code&gt;，使其接受下标访问即可。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;constexpr proxy_t&amp;lt;T...&amp;gt; operator[] (long long index) const noexcept
{ return proxy_t &amp;lt;T...&amp;gt;(*this, index &amp;gt;= 0 ? index : sizeof...(T) + index); }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里返回一个 &lt;code&gt;pytuple::proxy_t&lt;/code&gt;，用于接收需要转换的类型为参数。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template &amp;lt;typename... pt&amp;gt;
class proxy_t {
    friend pytuple;
    friend class pytuple::iterator;

    std::tuple&amp;lt;pt...&amp;gt; const&amp;amp; m_ptr;
    size_t index;
    constexpr proxy_t (std::tuple&amp;lt;pt...&amp;gt; const&amp;amp; m, size_t i) noexcept
        : m_ptr (m), index (i) { } 

public:
    template&amp;lt;typename cv&amp;gt;
    inline operator cv const () const{
        cv v; /* ignore Int-unini, may not have default constructor */
        mcl_tuple_visitor&amp;lt;cv&amp;gt; (index, m_ptr, &amp;amp;v);
        return v;
    }
    friend inline std::ostream&amp;amp;
    operator&amp;lt;&amp;lt; (std::ostream&amp;amp; os, proxy_t&amp;lt;pt...&amp;gt; const&amp;amp; rhs) {
        mcl_tuple_printer&amp;lt;std::ostream&amp;gt; (rhs.index, rhs.m_ptr, &amp;amp;os);
        return os;
    }
    friend inline std::wostream&amp;amp;
    operator&amp;lt;&amp;lt; (std::wostream&amp;amp; os, proxy_t&amp;lt;pt...&amp;gt; const&amp;amp; rhs) {
        mcl_tuple_printer&amp;lt;std::wostream&amp;gt; (rhs.index, rhs.m_ptr, &amp;amp;os);
        return os;
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;元组遍历&lt;/h2&gt;
&lt;p&gt;在 Python 中，我们可以像这样对元组进行遍历输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tu = (&apos;Thank&apos;, &apos;you&apos;, &apos;for&apos;, &apos;making&apos;, &apos;use&apos;, &apos;of&apos;)
for i in tu:
    print (i)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 C++ 中也有基于范围的循环。我们只需要提供 &lt;code&gt;begin&lt;/code&gt; 和 &lt;code&gt;end&lt;/code&gt; 方法即可利用这一语法糖。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;constexpr iterator begin () const noexcept{ return iterator {*this, 0}; }
constexpr iterator end   () const noexcept{ return iterator {*this, sizeof...(T)}; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里返回一个 &lt;code&gt;pytuple::iterator&lt;/code&gt; 用于进行索引。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class iterator {
    std::tuple&amp;lt;T...&amp;gt; const&amp;amp; m_ptr;
    size_t index;
    constexpr iterator (std::tuple&amp;lt;T...&amp;gt; const&amp;amp; m, size_t i) noexcept
        : m_ptr (m), index (i) { } 
    friend pytuple;
public:
    constexpr pytuple::proxy_t&amp;lt;T...&amp;gt; operator* () noexcept
    { return pytuple::proxy_t&amp;lt;T...&amp;gt;(m_ptr, index); }
    constexpr bool operator!= (iterator const&amp;amp; rhs) const noexcept
    { return this-&amp;gt;index != rhs.index; }
    inline iterator&amp;amp; operator++ () noexcept{ ++ index; return *this; }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如此以来，我们就可以在 C++ 中这样写：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;auto tp = mcl::maktuple (&apos;this&apos;, &apos;graphics&apos;, &apos;library&apos;)
for (auto i: tp)
    std::cout &amp;lt;&amp;lt; i &amp;lt;&amp;lt; &apos; &apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果只是用于输出，可以利用递归来优化。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private:
    template &amp;lt;typename char_type, size_t i = 1&amp;gt; 
    constexpr typename std::enable_if&amp;lt;
        (i &amp;gt;= sizeof...(T)), std::basic_ostream&amp;lt;char_type&amp;gt;&amp;amp; &amp;gt;::type
    str_ (std::basic_ostream&amp;lt;char_type&amp;gt;&amp;amp; ss) const noexcept
    { return ss &amp;lt;&amp;lt; &quot;)&quot;; }

    template &amp;lt;typename char_type, size_t i = 1&amp;gt;
    constexpr typename std::enable_if&amp;lt;
        (i &amp;lt; sizeof...(T)), std::basic_ostream&amp;lt;char_type&amp;gt;&amp;amp; &amp;gt;::type
    str_ (std::basic_ostream&amp;lt;char_type&amp;gt;&amp;amp; ss) const noexcept
    { return ss &amp;lt;&amp;lt; &quot;, &quot; &amp;lt;&amp;lt; std::get&amp;lt;i&amp;gt;(*this), str_&amp;lt;char_type, i + 1&amp;gt;(ss); }

public:
    friend constexpr std::ostream&amp;amp;
    operator&amp;lt;&amp;lt; (std::ostream&amp;amp;  os, pytuple&amp;lt;T...&amp;gt; const&amp;amp; tu) {
        return sizeof...(T) ? (
            os &amp;lt;&amp;lt; &apos;(&apos; &amp;lt;&amp;lt; std::get&amp;lt;0&amp;gt;(tu),
            (sizeof...(T) == 1) ? (os &amp;lt;&amp;lt; &quot;,)&quot;) : (tu.str_ (os))
        ) : (os &amp;lt;&amp;lt; &quot;()&quot;);
    }
    friend constexpr std::wostream&amp;amp;
    operator&amp;lt;&amp;lt; (std::wostream&amp;amp; os, pytuple&amp;lt;T...&amp;gt; const&amp;amp; tu) {
        return sizeof...(T) ? (
            os &amp;lt;&amp;lt; &apos;(&apos; &amp;lt;&amp;lt; std::get&amp;lt;0&amp;gt;(tu),
            (sizeof...(T) == 1) ? (os &amp;lt;&amp;lt; L&quot;,)&quot;) : (tu.str_ (os))
        ) : (os &amp;lt;&amp;lt; L&quot;()&quot;);
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样以来，输出一个元组，我们还可以写：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;auto tp = mcl::maktuple (&quot;GPL-3.O&quot;,&quot;©&quot;, &quot;Yukino Amamiya&quot;);
std::cout &amp;lt;&amp;lt; tp &amp;lt;&amp;lt; std::endl;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;至此，我们就实现了一个简易的 Python 元组。它可以遍历、可以嵌套、可以按下标访问。当然，Python 元组还有很多其他特性，比如切片、复制等，由于这些功能只能通过运行时闭包实现，极大的降低了效率，而图形库 &lt;code&gt;mclib&lt;/code&gt; 中元组仅是作为部分函数的返回值使用，用不到这么多功能，故并未支持。&lt;/p&gt;
&lt;p&gt;这里列出支持的操作（注释为输出结果）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;auto t1 = mcl::maktuple (1, 2, &quot;Hello&quot;);
auto t2 = mcl::maktuple (0.2, 99, &quot;World&quot;);
auto t3 = mcl::maktuple (t1);
std::cout &amp;lt;&amp;lt; t1 &amp;lt;&amp;lt; &apos;\n&apos;;               // (1, 2, Hello)
std::cout &amp;lt;&amp;lt; t3 &amp;lt;&amp;lt; &apos;\n&apos;;               // ((1, 2, Hello),)
std::cout &amp;lt;&amp;lt; mcl::cmp(t2, t1) &amp;lt;&amp;lt; &apos;\n&apos;; // -1
std::cout &amp;lt;&amp;lt; t1[-1] &amp;lt;&amp;lt; &apos;\n&apos;;           // Hello
std::cout &amp;lt;&amp;lt; t2[2] &amp;lt;&amp;lt; &apos;\n&apos;;            // World
std::cout &amp;lt;&amp;lt; mcl::len(t2) &amp;lt;&amp;lt; &apos;\n&apos;;     // 3
for (auto i: t2)
    std::cout &amp;lt;&amp;lt; i &amp;lt;&amp;lt; &apos;\n&apos;;            // 0.2\n99\nWorld
float m = t2[-2];                      // m = 99.f
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>【转载】不做“语言律师”！</title><link>https://iamyukino.cn/blog/posts/language-lawyer/</link><guid isPermaLink="true">https://iamyukino.cn/blog/posts/language-lawyer/</guid><description>这是旧版博客的一条Valine留言，让雨雪印象深刻。什么是“语言律师”呢？</description><pubDate>Tue, 05 Oct 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;:::TIP
本文转载自 &lt;a href=&quot;https://blog.51cto.com/grady/588234&quot;&gt;https://blog.51cto.com/grady/588234&lt;/a&gt;&lt;br /&gt;
不做半桶水的 C++ “语言律师”（language lawyers）！
:::&lt;/p&gt;
&lt;p&gt;有人在 Slashdot 上问了一个问题：什么认证最好？（Best Certifications To Get?）提问者说他想在 IT 领域有所提升，所以想要考取一些有价值的认证，以便简历可以更好看一些。 Slashdot 上的人给出了很多回答，其中有一个回答很精彩，而且还被投递到了 Hacker News ，回答的 &lt;a href=&quot;http://ask.slashdot.org/comments.pl?sid=2198700&amp;amp;cid=36293622&quot;&gt;原文在这里&lt;/a&gt;，我简单翻译如下（翻译仓促，建议直接阅读原文）：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我曾经是一名程序员，后来去做了日本的一名英语教师。日本是一个很有意思的地方，这里的学生知道懂得很多英语，但是却不能在麦当劳顺利点一杯饮料。在编程领域也有一些这样的人，我们把他们叫做“语言律师”（language lawyers）。这种人可以回答有关编程语言的任何问题，但是却没有真实的编程能力；他们可以很容易通过面试，但是当他们真正编程的时候却非常令人失望。这些程序员和我的学生非常像：他们有 5000 词汇量，知道书上写的所有语法规则，但就是不会说英语。&lt;/p&gt;
&lt;p&gt;我现在的观点是编程就像写作。有关编程的很多概念并不困难，它之所以困难是因为我们不善于“写作”。大部分的程序员并不能做到“流利”，也没有写得“流利”的意愿。他们不阅读别人的代码，不认识也不会使用“成语”，思维模式不是编程语言的思维模式（They don’t think in the programming language）。很多代码之所以烂是因为它的“流利”程度就好像3岁小孩写的小说。这也是为什么我们的程序呈现出不必要的复杂。&lt;/p&gt;
&lt;p&gt;我们教授编程的方法是错误的，那种方式和日本的英语老师教授英语的方式如出一辙。我们只是传授有关编程的一切概念，然后期望学生可以从那些东西当中自发地学会如何编程。&lt;/p&gt;
&lt;p&gt;在语言学习方面有一个假说：所有的语言只有在被理解的情况下才会真正被吸收。也就是说，如果你可以根据你已经知道的或者上下文理解你听到或读到的词句，那么你就会吸收它。解释不能帮助你吸收语言。我相信这对编程而言也是正确的。我们应该让学生寖浸在好的代码中，让他们不通过解释来真正吸收编程的能力。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我个人很认同这个回答中表达的观点。如果文中所说是真的，那么中国的英语教育和日本的英语教育实在没有什么差别，教的都是“哑巴英语”。我们学习英语的时候都是偏重于“单词意思”、“语法”等东西，结果学习完之后还是中文思维，听到英文第一反应是先把它翻译成中文，然后再去理解它的意思。因为有了 “翻译”这个环节，听和说几乎都会慢半拍，怎么可能流利呢？编程也是一样，你的脑子里记的都是一些所谓的“代码规范”、“最佳实践”⋯⋯ 写代码的时候总是先想到这些，而且每每以“符合规范”为荣。我并不是说规范不好，但是如果你不能把这些规范“忘掉”，写出来的代码虽然没有什么毛病，但肯定不会让人觉得很妙。一个写作的人如果在写作的时候总是念念不忘语法规则，也肯定写不出好看的文章。&lt;/p&gt;
&lt;p&gt;语言是用来使用的，编程语言也是一样。不少人喜欢在那里死记硬背一些语言特性然后去和别人谈论，有时候还美其名曰“注重细节”，这对提高编程能力完全没有用处。说来惭愧，我一直都是一个 language lawyer，编程书籍和文档都看了不少，但真正做出来的东西就实在太少了。有时候想要看别人的代码然后模仿，但是看懂别人的代码之后就觉得自己已经了解，然后就失去了动手的兴趣。做东西也是一样，当我做到80%的时候就会发现剩下的自己都知道怎么做，于是很容易就半途而废。&lt;/p&gt;
&lt;p&gt;“知道怎么做”和“真正去做并且完成”完全是两回事，知道所有的单词也未必可以写出一个优美的句子。希望以后在英语和编程学习方面都可以摆脱中国语言教育留下的坏习惯。&lt;/p&gt;
&lt;p&gt;本文转载自 &lt;a href=&quot;https://blog.51cto.com/grady/588234&quot;&gt;https://blog.51cto.com/grady/588234&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>校编程社的第一次上课内容准备</title><link>https://iamyukino.cn/blog/posts/prep-for-programming-club/</link><guid isPermaLink="true">https://iamyukino.cn/blog/posts/prep-for-programming-club/</guid><description>阴差阳错当上了校信息编程社的社长，还要自己备课？雨雪对着C++14的语法胡言乱语些什么呢？</description><pubDate>Mon, 04 Oct 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;:::TIP
&lt;a href=&quot;../language-lawyer/&quot;&gt;不做“语言律师”&lt;/a&gt;（language lawyers）！本文仅供抛砖引玉，请勿深究。&lt;br /&gt;
本文内容遵循 Effective C++ 所提及的代码规范，供社团课备课所用。
:::&lt;/p&gt;
&lt;p&gt;由于各种原因，我最终还是决定以后的社团课主要以 C++ 语言的学习为主，而且大部分时间都会由我来讲课而不是像之前一样放相关教学视频。现在学校电脑上还没有装环境，今天我准备是单纯以我讲为主。&lt;/p&gt;
&lt;p&gt;嗯我们来看一下屏幕。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// hello.cpp
#include &amp;lt;bits/stdc++.h&amp;gt;

using namespace std;

int main(void) {

    cout &amp;lt;&amp;lt; &quot;Hello, World!&quot; &amp;lt;&amp;lt; endl;

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;屏幕上这个是 C++ 语言代码的基本框架。我们现在还不必详细一一探究每一行分别是什么意思，只需要把它当作是一个模板，每次写代码时复制上去即可。我们现在来运行一下代码。&lt;/p&gt;
&lt;p&gt;可以看到，程序向控制台输出了 &lt;code&gt;Hello, World!&lt;/code&gt; 这一行字符串，说明我们的代码运行成功了。在这个社团课中，我所讲的都是在 &lt;code&gt;C++ 14&lt;/code&gt; 标准下的代码。C++ 是一门中级语言，她综合了高级语言和低级语言的许多特点，但她不是像 python 那样的解释型语言，而是一门编译式的语言，所以它从你编写的代码到可以直接运行的可执行文件往往需要进行四个步骤：&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;第一步，预处理&lt;/code&gt; 预处理负责处理头文件和注释，她本质上是一些指令，用以指示编译器在实际编译之前所需完成的预处理内容。预处理语句不属于C++语句。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;第二步，编译&lt;/code&gt; 编译器将生成汇编代码文件。因为她是使用静态类型的编程语言，所以类型检查就是在这里进行。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;第三步，汇编&lt;/code&gt; 对汇编代码文件进行汇编，以转成机器指令，从而生成目标文件。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;第四步，链接&lt;/code&gt; 一些动态链接库在这里合并到你的文件中。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;这样，你的代码就成功变为了可执行文件。&lt;/p&gt;
&lt;p&gt;学习编程是一回事，学习如何以某种语言设计并实现你所预期的程序则是另一回事。这种说法对 C++ 尤其适用。C++ 是在完全保留 C语言 语法之上对 C语言 许多不足之处进行扩充的一门编程语言，多种编程风格都被 C++ 所支持，巨大而变化多端的设计可以被直接表现出来，并且被有效实现出来。&lt;/p&gt;
&lt;p&gt;一组明智选择并被精心设计的 &lt;code&gt;classes&lt;/code&gt;，&lt;code&gt;functions&lt;/code&gt; 和 &lt;code&gt;templates&lt;/code&gt; 可使程序编写容易、直观、高效并且远离错误。我们来看一下这两段代码。&lt;/p&gt;
&lt;p&gt;以下代码仅作演示，不必记忆或理解。&lt;/p&gt;
&lt;p&gt;第一段代码是用 C语言 编写的。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// calc.c
#include &amp;lt;stdio.h&amp;gt;

int add(int a, int b) {

	return a + b;
}

int main() {

	printf ( &quot;1 + 1 = %d\n&quot;, add (1, 1) );
    
	return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这段代码暂且先不管每句语句是什么意思。我希望让程序计算 &lt;code&gt;1 + 1&lt;/code&gt;，程序输出了 2，说明确实能计算 &lt;code&gt;1 + 1 = 2&lt;/code&gt; 的功能。现在我们将代码中的 1 改为小数，来计算 &lt;code&gt;0.3 + 1.6&lt;/code&gt;，看看会发生什么。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[1;33m1[0m
--------------------------------
Process exited after [0;35m0.01506 seconds[0m with return [0;35mvalue 0[0m
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到，原本应该返回 &lt;code&gt;1.9&lt;/code&gt; 的代码返回了 1，在背后偷偷发生了隐式类型转换，这不是我们所想要的。换一个数据类型就要重写函数，十分不方便。而在 C++ 中，我们可以将这些 &lt;code&gt;int&lt;/code&gt; 改为 &lt;code&gt;auto&lt;/code&gt;，让编译器自动识别类型，就像这样。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;

template &amp;lt;typename _ta, typename _tb&amp;gt;

auto add (_ta const a, _tb const b)
-&amp;gt; decltype (a + b) { return a + b; }

int main () {

	std::cout &amp;lt;&amp;lt; &quot;0.3 + 1.6 = &quot; 
		&amp;lt;&amp;lt; add (0.3, 1.6) &amp;lt;&amp;lt; std::endl;
	return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;程序成功输出了 &lt;code&gt;1.9&lt;/code&gt;。当然，C++ 所提供的语法糖或强大功能还有很多，比如刚才这段程序，我们也可以用 &lt;code&gt;lambda&lt;/code&gt; 匿名函数，就像这样。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// calc2.cpp
#include &amp;lt;iostream&amp;gt;

auto add =
	[](auto a, auto b) {return a + b;} ;

int main () {

	std::cout &amp;lt;&amp;lt; &quot;0.3 + 1.6 = &quot;
		&amp;lt;&amp;lt; add (0.3, 1.6) &amp;lt;&amp;lt; std::endl;
	return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个社团课不是单纯告诉你编程算法，而是让你了解到 C++ 的许多语法特性。你可能曾经并不知道 C++ 是个编程语言或对它有某些使用经验，但我相信，在座的各位对数学上的公式等内容肯定都已经有了很高的掌握程度，于是我主要还是讲解这个语言本身，使你以后写出的软件易理解，易维护，可扩充，高效并且有着你所预期的行为。虽然你们下学期要学的是 python 而不是 C++，但是，可要知道，python 的第一个发行版本就是由 C 编写的，且里边的很多语法都与 C++ 有着很高的相似度。&lt;/p&gt;
&lt;p&gt;之前提到，C++ 完全保留了 C语言 的语法。其实，最开始的 C++ 只是在 C 的基础上加上了最基本的面向对象概念中的 &lt;code&gt;class&lt;/code&gt; 类，并取名为 “带类的C”，而后才慢慢发展成为一门强大的编程语言，一门多重范式的编程语言，一个同时支持过程形式、面向对象形式、元编程形式、泛型形式和函数形式的编程语言。它的名字 C++ 也是用的 C 中的自增运算符 &lt;code&gt;++&lt;/code&gt; 命名的。其实，如果 C++ 抛弃 C 的语法，不需要支持例如 C语言 八种整数类型等，那么 C++ 将远比现在简单。我最开始也是学 C ，但当我开始自己写图形库之后，才意识到在 C 中很多想要的都很难去实现，便转战 C++。当然，C++ 的成功也有 C语言 不小的功劳，比如 C++ 适合写底层，是因为 C语言 从内存模型等方面对底层操作提供了很多支持。&lt;/p&gt;
&lt;p&gt;不过，即使强大，但反而可能导致了不易入门。你需要学习掌握很多专业术语，这我也会在以后的社团课上逐一说明。比如在《Effective C++》书中提到的一些问题：你该选择 &lt;code&gt;inheritance&lt;/code&gt; 继承还是 &lt;code&gt;templates&lt;/code&gt; 模板，该选择 &lt;code&gt;public&lt;/code&gt; 继承还是 &lt;code&gt;private&lt;/code&gt; 继承还是 &lt;code&gt;composition&lt;/code&gt; 复合？该选择 &lt;code&gt;pass_by_value&lt;/code&gt; 以值传递，还是 &lt;code&gt;pass_by_reference&lt;/code&gt; 以指针传递？等等。即使在面试中，有人可能会说熟练使用 C++，但除了标准制定者之外，只有极少聪明人敢说精通。即使你完全知道该做什么，完全进入正轨，还是可能有些棘手。什么是 &lt;code&gt;assignment&lt;/code&gt; 操作符的适当返回型？什么是 &lt;code&gt;bind&lt;/code&gt; 绑定器？何时该令析构函数为 &lt;code&gt;virtual&lt;/code&gt; 虚拟？当 &lt;code&gt;operator new&lt;/code&gt; 无法找到足够内存空间时该如何行事？等等。&lt;/p&gt;
&lt;p&gt;我之前也说过，我所讲的内容都是基于 &lt;code&gt;C++1y&lt;/code&gt;（C++14）也就是14年指定的 C++ 标准。这是一个比较新的标准，可能有些编译器比如 VC6.0 等不能很好的支持。我这里主要用 Dev-C++，一个极为简陋但对初学者十分友好的编译器。我这里有这个编译器的安装包，如果想要可以下课后直接上来拷贝。我也会把这个编译器的安装包放群里，大家回去记得加一下群。&lt;/p&gt;
&lt;p&gt;现在我先来简单说一下输出吧。而对输出语句中符号各自含义什么的我会放到后面去讲，详细的输出语法我也会放在第二节课讲。在 C++ 中，在控制台输入的语句格式是: &lt;code&gt;cout &amp;lt;&amp;lt; 要输出的内容;&lt;/code&gt;。每个输出的内容之间用两个小于号分隔开。比如要输出数字 &lt;code&gt;256&lt;/code&gt; ，就写成 &lt;code&gt;cout &amp;lt;&amp;lt; 256;&lt;/code&gt;。换行最好用 &lt;code&gt;endl&lt;/code&gt;。当然也可以输出文字。输出常量字符串时要在输出的内容两边加上两个双引号。注意一定要用英文的双引号，不然电脑可不能识别。而且，只要是在 C 或 C++ 中，每句语句末尾都需要加上一个分号 &lt;code&gt;;&lt;/code&gt;，这代表语句的结束。&lt;/p&gt;
&lt;p&gt;嗯，输出暂且先说这么多，接下来我们来简单讲讲变量。这里的内容可能有点难，不过请不要担心，在第二课，对于这一部分，我会从最基础的和你讲起。&lt;/p&gt;
&lt;p&gt;在 C++ 中，一个变量就好比一个容器，你可以在这个容器里存放一些东西。你首先需要给她命名，这样你在需要她的时候就可以直接引用这个名字去访问容器。你只能给她取英文名，下划线或是数字，而且要保证不能有重名。（ &lt;code&gt;$&lt;/code&gt; 往往也被允许作为变量的名字，但我并不推荐你这么做，因为这可能并不具备可移植性 ）否则，计算机就不知道你是要叫谁。命名空间，便是为了解决这一问题而生。&lt;/p&gt;
&lt;p&gt;不过，这个容器是有一定限制的。变量，其实就是在内存中申请分配的一段连续的内存空间的名称，这个空间不可能做到无限大，所以你必须要指定她所占用的空间大小，或应该存放什么样的数据。当然，C++ 提供了 &lt;code&gt;new&lt;/code&gt; 关键字或可变长数组等，可以动态分配内存，在这里就暂且不提。（ 注意区别 Python 这样的动态类型语言 ）&lt;/p&gt;
&lt;p&gt;抛开 C语言 本身的语法，C++ 就和 Python 很像了。在她的世界里，几乎一切皆对象。其中，你指定变量的语句叫声明式。你通过声明式告诉编译器某个变量的名称和类型，但略去细节。注意对于 &lt;code&gt;int&lt;/code&gt; 这样的内置类型，除非是 &lt;code&gt;extern&lt;/code&gt;，否则都不是声明式。声明式的语法是：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;（[1;34m模板[0m [1;34m限制符[0m）[1;34m类型[0m 变量名 (: 继承类) ;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;就像这样：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// ref.cpp
extern int x;

void func (int a);

class point3d;

template &amp;lt;typename T&amp;gt;
class canvas;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;学过一点 C++ 的可能会问，&lt;code&gt;int&lt;/code&gt; 整型不是一个内置类型吗？为什么是一个对象？可能有些人会把“对象”一词保留给用户自定义类型，但并不如此。当然，现在还不必深究。&lt;/p&gt;
&lt;p&gt;而定义式的任务是提供给编译器一些声明式所遗漏的细节。对对象而言，定义式是编译器为对象分配内存的起点。定义式的语法是：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;([1;34m模板[0m)
([1;34m限制符[0m）[1;34m类型[0m
变量名 (: 继承类)
(函数或结构体)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;比如 &lt;code&gt;int num;&lt;/code&gt;。特别地，类型的定义式语法是：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// tdef.cpp
using 类型别名 = 原类型;

typedef 原类型 类型别名;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是，你光是让编译器给你分配内存，你什么也不干，显然没有意义。你往往需要在里面装东西，才能体现出他们的价值。给予对象初值，也就是第一次往里存放数据的过程叫初始化。如果是在说 C语言，我可能会和你讲，初始化的方法是在定义式后写上赋值语句，但 C++ 并不完全是这么实现的。在 C++ 中，初始化由构造函数执行。每个类型编译器已经为你提供了对应的 &lt;code&gt;default&lt;/code&gt; 构造函数。姑且先不管函数是什么，就当她是很多语句的组合，及构造函数就当她是赋值语句的组合。我们先来看看代码长什么样。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;[注]&lt;/strong&gt; &lt;em&gt;所谓 &lt;code&gt;default&lt;/code&gt; 构造函数是一个可被调用而不带任何实参的构造函数，这样的构造函数要不是没有参数，要不是就是每个参数就有缺省值。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;fruit apple{6.5};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在这句语句中，我将 &lt;code&gt;apple&lt;/code&gt; 作为了变量的名字，并告诉编译器其类型是 &lt;code&gt;fruit&lt;/code&gt;。然后我在名字后边写了一对大括号，里面写了一个数字 &lt;code&gt;6.5&lt;/code&gt;，也就是我要将 &lt;code&gt;6.5&lt;/code&gt; 作为 &lt;code&gt;apple&lt;/code&gt; 的初值。这个花括号的专业术语是初始化列表。当然，你也可以用小括号，直接表面要调用构造函数，或是直接用 C语言 的语法，即赋值，就像这样：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// vdef.cpp
int a(5);

double a = 3.14;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其中，&lt;code&gt;int&lt;/code&gt; 表示整型，即可以存储从 &lt;code&gt;-2^31&lt;/code&gt; 到 &lt;code&gt;2^31-1&lt;/code&gt; 的整数。&lt;code&gt;double&lt;/code&gt; 表示双精度浮点型，即可以存储小数（&lt;code&gt;double&lt;/code&gt; 在计算机内部是以科学计数法的形式存储的）。还需要记住两个类型，就是 &lt;code&gt;char&lt;/code&gt; 和 &lt;code&gt;std::string&lt;/code&gt;。&lt;code&gt;char&lt;/code&gt; 表示字符，表示是一个符号（见 ASCII 编码系统）。&lt;code&gt;std::string&lt;/code&gt; 表示字符串，封装了 C语言 中的字符指针 &lt;code&gt;char*&lt;/code&gt;，表示是一段文字。直接写 &lt;code&gt;char&lt;/code&gt; 的单个字符时，两边要用英文单引号将其包含在内；直接写 &lt;code&gt;string&lt;/code&gt; 的一个常量字符串时，两边要用双引号包含。注意如果要换多行写同一个字符串，每行的开头和结尾都需要写双引号，就像这样：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;string str = &quot;Welcome &quot;
  &quot;to Our Programming&quot; // 多个常量字符串
  &quot; Club!&quot;; // 编译器会将其自动合并为一个
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C++ 也提供了不必每行都写双引号的多行字符串，不是很常用，就像这样：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;string str = R&quot;(Welcome
  to Our Programming
  Club!)&quot;; // 多行字符串
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们可以试试输出刚才定义好的变量：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;bits/stdc++.h&amp;gt;
using namespace std;
int main () {

	int i { 2021 };

	char c (&apos;!&apos;);

	string str = &quot;Welcome &quot;
	&quot;to Our Programming&quot;
	&quot; Club &quot;;

	cout &amp;lt;&amp;lt; str &amp;lt;&amp;lt; i &amp;lt;&amp;lt; c &amp;lt;&amp;lt; endl;
	return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;程序成功输出了以下结果：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[1;33mWelcome to Our Programming Club 2021![0m
--------------------------------
Process exited after [0;35m0.01415 seconds[0m with return [0;35mvalue 0[0m
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;今天的社团课我就先上到这里，因为没有环境，暂时就不布置作业了，接下来还有一点时间就留给大家写作业。我上面会放一个社团的宣传视频，你们可以看看。&lt;/p&gt;
&lt;p&gt;:::important[参考文献]
&lt;strong&gt;[1]&lt;/strong&gt; &lt;em&gt;Effective C++: 55 Specific Ways to Improve Your Programs and Designs&lt;/em&gt;，3 / (美)梅耶 (Meyers, S.) 著
:::&lt;/p&gt;
</content:encoded></item><item><title>希沃白板内置电脑如何升级windows11</title><link>https://iamyukino.cn/blog/posts/how-to-install-win11-in-seewo/</link><guid isPermaLink="true">https://iamyukino.cn/blog/posts/how-to-install-win11-in-seewo/</guid><description>希沃白板升级windows11，只需三步！</description><pubDate>Tue, 31 Aug 2021 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;引言&lt;/h2&gt;
&lt;p&gt;暑假过去的好快啊，不知不觉，新的一学期开始了！令我十分惊喜的是，学校的电脑也算是因为换成希沃而变为了windows10，不过。。。微软都出windows11了，windows10怎么够呢？于是。。。:spoiler[&lt;s&gt;学校电脑惨遭毒手&lt;/s&gt;]&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;希沃白板升级windows11，只需三步！&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;第一步 修改遥测级别&lt;/h2&gt;
&lt;p&gt;开始栏中搜索并运行Windows PowerShell。&lt;br /&gt;
在PowerShell内输入以下内容：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$path = &quot;HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\DataCollection&quot;
# Telemetry level: 1 - basic, 3 - full
$value = &quot;3&quot;
New-ItemProperty -Path $path -Name AllowTelemetry -Value $value -Type Dword -Force
New-ItemProperty -Path $path -Name MaxTelemetryAllowed -Value $value -Type Dword -Force
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;第二步 加入预览体验计划&lt;/h2&gt;
&lt;p&gt;加入Windows预览体验计划。&lt;br /&gt;
设置 -&amp;gt; 系统与更新 -&amp;gt; Windows预览体验计划。&lt;br /&gt;
创建并登录Mircosoft账户 -&amp;gt; Beta渠道 -&amp;gt; 确定&lt;/p&gt;
&lt;h2&gt;第三步 下载和安装&lt;/h2&gt;
&lt;p&gt;打开设置 -&amp;gt; 系统与更新。&lt;br /&gt;
按照提示检测、下载、安装、重启、...直到更新至最新版即可。&lt;/p&gt;
</content:encoded></item><item><title>98的倒数竟是二的次方表？</title><link>https://iamyukino.cn/blog/posts/is-reciprocal-98-table-of-pow-2/</link><guid isPermaLink="true">https://iamyukino.cn/blog/posts/is-reciprocal-98-table-of-pow-2/</guid><description>还是初中生的雨雪什么也不懂，意外发现了好玩的东西，弯弯绕绕试图证明...《数学史话与美学》课程作业</description><pubDate>Thu, 18 Mar 2021 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;发现&lt;/h2&gt;
&lt;p&gt;在一次意外的运算中，我突然发现整数的倒数似乎十分有规律，尤其是 $\frac{1}{98}$，它的小数部分居然直接是二的次方表！于是我便开始尝试通过证明，来研究这一规律。&lt;/p&gt;
&lt;p&gt;那这只是一个特例吗？我先试着使用计算器计算了与之十分相近的数：&lt;/p&gt;
&lt;p&gt;$\frac{1}{97} = 0.010309\ldots$、 $\frac{1}{96}=0.0104\ldots$ 然后作出了一个猜测：$\frac{1}{n}$ 的小数部分是 $(100 - n)$ 的次方表。但是，很容易想到，当 $n \geq 100$ 时原式无意义。
于是我又计算了 $\frac{1}{998} = 0.001002004\ldots$、 $\frac{1}{9998} = 0.000100020004\ldots$ 这样以来就十分明确了，$n$ 的位数决定每个次方数占用位数，$n$ 的相对大小决定次方的底数。&lt;/p&gt;
&lt;h2&gt;证明&lt;/h2&gt;
&lt;p&gt;:::caution[此证明过程存在且不限于：错误推导、冗余构造、复杂定义、无效证明]
等情况，受限于札记撰写时作者初中的知识局限，是作者当时心路历程的完整体现。&lt;br /&gt;
&lt;strong&gt;为保证原始文章存档的完整性，本页未对文章做出任何改动。&lt;/strong&gt;
:::&lt;/p&gt;
&lt;p&gt;我尝试将 &lt;code&gt;min&lt;/code&gt; 函数构造为一个包含两个次方数且这两个数被无限累加的恒等式，先不妨假设有 $a, b \in N^*$，可得
$$
\begin{aligned}
&amp;amp;\min\left(a, b\right) \
&amp;amp;=\left{ \begin{array}{}
a - 0 &amp;amp; a \leq b \
a - (a - b) &amp;amp; a &amp;gt; b
\end{array} \right. \
&amp;amp;=a - f^{-1}(x),;f(x) = \left{ \begin{array}{}
\infty &amp;amp; a \leq b \
\frac{1}{a - b} &amp;amp; a &amp;gt; b
\end{array} \right.
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;对于正无穷，使原单调求和数列在 $a &amp;gt; b$ 时不收敛即可，故只需要构造函数在 $a \leq b$ 上 $\to\frac{1}{a-b}$&lt;/p&gt;
&lt;p&gt;根据希望构造的式子特点，利用求和函数构造有 $f(x) = \sum_1^\infty\frac{b^{i-1}}{a^i}$&lt;/p&gt;
&lt;p&gt;得 原式 $= a - (\sum_1^\infty\frac{b^{i-1}}{a^i})^{-1}$&lt;/p&gt;
&lt;p&gt;由于 $n$ 的位数 $=$ 每个次方数占用位数，故将 $a = 10^{\lfloor \lg b \rfloor}$ 带入，显然此时 $\min\left(a, b\right) = a$，&lt;/p&gt;
&lt;p&gt;既有 $10^{\lfloor \lg b \rfloor} - b = (\sum_1^\infty\frac{b^{i-1}}{10^{i \cdot \lfloor\lg b \rfloor}} )^{-1}$ 即 $\sum_1^\infty \frac{b^{i-1}}{10^{i\cdot \lfloor\lg b \rfloor}} = (10^{\lfloor \lg b \rfloor} - b)^{-1}$&lt;/p&gt;
&lt;p&gt;构造完成，得证。&lt;/p&gt;
&lt;p&gt;以上在 $a,b \in \left(0, +\infty\right)$ 时均成立。通过这个通式，我们便可以很容易口算一个数倒数的近似值。其中，求和函数在数学表达方面提供了很大方便和帮助。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;以上猜想是我在五年级时开始学习编程后提出的问题，当时便是利用求和先提出了这一猜想的通式然后反过来慢慢证明出的。猜想是创造数学思想方法的重要途径。作为初中学生，在还未学习过的领域，证明一些复杂定理似乎很难，但我们可以先根据寻找规律的特点，写出猜想通式先记下来，毕竟这也是很关键的一步啊。探索试验、类比联想、归纳构造审美以及它们之间的组合什么的，那就等待时间的沉淀慢慢探索吧。&lt;/p&gt;
</content:encoded></item><item><title>在CASIO计算器中，也能编程？——计算器简单算法实现</title><link>https://iamyukino.cn/blog/posts/coding-in-calculator/</link><guid isPermaLink="true">https://iamyukino.cn/blog/posts/coding-in-calculator/</guid><description>不使用BUG也能在CASIO 991CN计算器(高考可以带)里实现基础的编程控制语句！《数学史话与美学》课程作业</description><pubDate>Thu, 04 Mar 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;现在常在考试里用的 &lt;code&gt;CASIO fx-991CN X&lt;/code&gt; 计算器所提供的计算功能，实际上可以实现很多常用编程算法。虽然由于计算器的局限性，对于非顺序结构的程序的实现较为复杂，但是在特定的场景中，计算器也有其方便之处。本篇杂记主要就是针对这一型号计算器在计算器上编程的一些技巧的研究。由于此型号计算器至多输入 &lt;code&gt;199&lt;/code&gt; 个字符，故本文不具备实用性。&lt;/p&gt;
&lt;h2&gt;运算符与赋值&lt;/h2&gt;
&lt;p&gt;常用运算符 &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt;, &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;^&lt;/code&gt; 有直接对应的符号，整除对应 &lt;code&gt;÷R&lt;/code&gt; 键。&lt;code&gt;mod&lt;/code&gt; 取余可以通过 $a \bmod b = a - \left(a \mid b\right) \times b$ 来表示，即为 $A - A \mathbin{\div!\text{R}} B \times B$ 。同理，&lt;code&gt;Int&lt;/code&gt; 取整可以写成 $A \mathbin{\div!\text{R}} 1$。&lt;/p&gt;
&lt;h3&gt;整除限制&lt;/h3&gt;
&lt;p&gt;如果被除数和除数有小数位数大于三位的，或有负数，或有大于等于 $10^{10}$ 的数，则不会输出取整后的结果。于是只能通过计算精度舍入来解决这一问题。&lt;/p&gt;
&lt;p&gt;以下是改正后的取余代码：&lt;/p&gt;
&lt;p&gt;$$
\begin{aligned}
&amp;amp;x \div 10^{12} + 1 \rightarrow x\colon \&lt;/p&gt;
&lt;p&gt;&amp;amp;(x - 1) \times 10^{12} \rightarrow x\colon \&lt;/p&gt;
&lt;p&gt;&amp;amp;10 - \text{Rnd}(\log(x + 1) + 1) \times 10^6 \mathbin{\div!\text{R}} 10^6 \rightarrow y\colon \&lt;/p&gt;
&lt;p&gt;&amp;amp;x \times 10^y \mathbin{\div!\text{R}} 10^y \rightarrow y
\end{aligned}
$$&lt;/p&gt;
&lt;h3&gt;变量赋值&lt;/h3&gt;
&lt;p&gt;在计算器中，变量有 &lt;code&gt;A&lt;/code&gt;, &lt;code&gt;B&lt;/code&gt;, &lt;code&gt;C&lt;/code&gt;, &lt;code&gt;D&lt;/code&gt;, &lt;code&gt;E&lt;/code&gt;, &lt;code&gt;F&lt;/code&gt;, &lt;code&gt;x&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;, &lt;code&gt;M&lt;/code&gt;，以下是对变量赋值的三种方法。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;所有变量的赋值都可用 &lt;code&gt;STO&lt;/code&gt; 键。&lt;/p&gt;
&lt;p&gt;注意当输入 &lt;code&gt;STO&lt;/code&gt; + 变量名 后，&lt;code&gt;→ 变量&lt;/code&gt; 会被计算器放到全部语句的最后，同时计算结果。所以每当输入 &lt;code&gt;STO&lt;/code&gt; + 变量名 后都要按一下 &lt;code&gt;AC&lt;/code&gt; + &lt;code&gt;左键&lt;/code&gt;，以恢复原式。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;变量 &lt;code&gt;M&lt;/code&gt; 自带累加器功能，可利用 &lt;code&gt;M+&lt;/code&gt; 和 &lt;code&gt;M-&lt;/code&gt; 来对 M 赋值。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;利用 &lt;code&gt;Pol&lt;/code&gt; 和 &lt;code&gt;Rec&lt;/code&gt; 组合可实现对于 &lt;code&gt;x&lt;/code&gt;、&lt;code&gt;y&lt;/code&gt; 的组合赋值。CASIO 计算器提供了这两个用于极坐标和角坐标互转的函数，我们可以利用这一对函数来实现同时对 &lt;code&gt;x&lt;/code&gt;、&lt;code&gt;y&lt;/code&gt; 赋值，比如想将 &lt;code&gt;2&lt;/code&gt; 赋值给 &lt;code&gt;x&lt;/code&gt;、&lt;code&gt;3&lt;/code&gt; 赋值给 &lt;code&gt;y&lt;/code&gt;，可以写 &lt;code&gt;Rec(Pol(2, 3), y)&lt;/code&gt;，不过有一点需要注意，当且仅当要赋的两个值同时为 &lt;code&gt;0&lt;/code&gt; 时会出现数学错误。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;顺序与循环结构&lt;/h2&gt;
&lt;p&gt;编程中实现一个功能往往需要多条语句分步执行。计算器提供了 &lt;code&gt;:&lt;/code&gt; 冒号用来分割语句。在计算器中输入&lt;code&gt;[1]&lt;/code&gt; &lt;code&gt;[ALPHA]&lt;/code&gt; &lt;code&gt;[∫□]&lt;/code&gt; &lt;code&gt;[2]&lt;/code&gt;，然后多次按等号，你会发现结果依次输出 &lt;code&gt;1&lt;/code&gt;、&lt;code&gt;2&lt;/code&gt;、&lt;code&gt;1&lt;/code&gt;、&lt;code&gt;2&lt;/code&gt;、... ，这就说明 &lt;code&gt;1&lt;/code&gt; 和 &lt;code&gt;2&lt;/code&gt; 这两条语句被依次且多次执行了。这也是无限循环在计算器里的基本框架。&lt;/p&gt;
&lt;h3&gt;问题 1：求极限&lt;/h3&gt;
&lt;p&gt;当你输入一组赋值语句后，比如希望实现以下内容：&lt;/p&gt;
&lt;p&gt;$$ \forall a_0,a_1 \in (0, +\infty), \text{求} \lim\limits_{i \to +\infty} a_i \quad (\text{Ans} = 4) $$
$$
a_i=\left{ \begin{array}{}
\frac{a_{i-1} + a_{i-2}}{2} &amp;amp; i\text{为大于1的偶数} \
\sqrt{a_{i-1}} - \sqrt{a_{i-2}} &amp;amp; i\text{为大于1的奇数} \
\end{array} \right.
$$&lt;/p&gt;
&lt;p&gt;时遇到问题：&lt;/p&gt;
&lt;p&gt;先赋初值 &lt;code&gt;36 →A&lt;/code&gt;，&lt;code&gt;24 →B&lt;/code&gt; 再写 $(A + B) ÷ 2 \rightarrow A: \sqrt{A} + \sqrt{B} \rightarrow B$ 在输入 &lt;code&gt;→A&lt;/code&gt; 时 &lt;code&gt;A&lt;/code&gt; 已经被改变。此时我们可以在式子最前面加 &lt;code&gt;1:&lt;/code&gt;，变成 $1 : (A + B) \div 2 \rightarrow A : \sqrt{A} + \sqrt{B} \rightarrow B$ 即可解决这一问题。当然，在输完表达式后可将 &lt;code&gt;1:&lt;/code&gt; 删去以减少按等号的次数。&lt;/p&gt;
&lt;h3&gt;问题 2：Fibonacci 数列&lt;/h3&gt;
&lt;p&gt;Fibonacci 数列 $a_i = a_{i-1} + a_{i-2}$ 如何实现。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;$1 \rightarrow B,;1 \rightarrow C;;$ // 把 B、C 的初值设为1&lt;br /&gt;
$ do;B \rightarrow A : C \rightarrow B : A + B \rightarrow C;;$ // C 即为数列的值&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;利用对于 $x_0 = x_1 = 1,;i - 2 \in N$&lt;br /&gt;
有 ${x_i = x_i-1} + x_{i-2} = \sum_1^{i-2}x_n + 1$ 可化为：&lt;br /&gt;
$do;M + 1 - Ans;\mathbin{\text{M}!+};;$ // 记得初始化保证 M = Ans = 0&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;利用递推表达式的特殊性可化为：&lt;br /&gt;
$ \text{Rec}(\text{Pol}(1, 1), y)$&lt;br /&gt;
$do;\text{Rec}(\text{Pol}(y, x + y), y)$&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;条件分支&lt;/h2&gt;
&lt;p&gt;既然计算器不能实现跳过语句，那又如何实现如 &lt;code&gt;if (a &amp;gt;= 0) c = b;&lt;/code&gt; 这样的判断分支语句呢？很容易想到的方法是在满足 $a \geq 0$ 时将 &lt;code&gt;b&lt;/code&gt; 赋值给 &lt;code&gt;c&lt;/code&gt;，否则就将 &lt;code&gt;c&lt;/code&gt; 赋值给自身。如果事先已经判断了 $a \geq 0$ 是否成立，成立就把 &lt;code&gt;d&lt;/code&gt; 设为 &lt;code&gt;1&lt;/code&gt;，否则就把 &lt;code&gt;d&lt;/code&gt; 设为 &lt;code&gt;0&lt;/code&gt;，那只需要写 $db + (1 - d)c \rightarrow c$ 即可。可是如何实现判断 $a \geq 0$ 呢？你可能会想到利用 $\left|a\right| \div a \rightarrow d$，但是这个函数定义域不为 &lt;code&gt;R&lt;/code&gt;。为了使其定义域为 &lt;code&gt;R&lt;/code&gt;，我们便需要保证 $a \neq 0$，一个很巧妙的方法是取整，像如下这样：&lt;/p&gt;
&lt;h3&gt;代码实现&lt;/h3&gt;
&lt;p&gt;$$
\begin{aligned}
&amp;amp; (-a - 1 + 1 + a) \times 10^{90} + a \rightarrow d:  \
&amp;amp; ((-d - 1) \div 10^{12} + 1 - 1) \times 10^{12} \rightarrow d: \
&amp;amp; .5 - (d + .5) \div \left|d + .5\right| \div 2 \rightarrow d:  \
&amp;amp; db + (1 - d)c \rightarrow c
\end{aligned}
$$&lt;/p&gt;
&lt;p&gt;就实现了 &lt;code&gt;if (a &amp;gt;= 0) c = b;&lt;/code&gt; 的功能。
同理，如果要再加上个 &lt;code&gt;else c = e;&lt;/code&gt; 只需要
把最后一句改成 $db + (1 - d)e \rightarrow c$。如果条件
是 &lt;code&gt;if (a == 0)&lt;/code&gt;，利用 $a = 0 \Leftrightarrow -|a| \geq 0$ 即可。&lt;/p&gt;
&lt;h3&gt;关于循环&lt;/h3&gt;
&lt;p&gt;计算器里编程，就像在整个程序外有个默认的死循环，而你就是在这个死循环中写程序。如果你希望结束程序，你只能通过“错误”来终止循环。这不需要复杂的条件判断，只需要利用根号大于等于 0、真数大于 0、分母不等于 0 等的特点来产生错误，循环便终止了。然后在退出“数学错误”界面后按 &lt;code&gt;SHIFT&lt;/code&gt; &lt;code&gt;STO&lt;/code&gt; 即可查看最终结果。即使是顺序结构的程序，也最好要在末尾加上一句 &lt;code&gt;: 0 ÷ 0&lt;/code&gt; 来结束程序。&lt;/p&gt;
</content:encoded></item><item><title>雨雪小学的时候写的日记作文</title><link>https://iamyukino.cn/blog/posts/childhood-diary-archives/</link><guid isPermaLink="true">https://iamyukino.cn/blog/posts/childhood-diary-archives/</guid><description>翻箱倒柜的雨雪翻出了小时候的作文，好怀念呀～会写些什么呢？</description><pubDate>Mon, 16 Sep 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;好怀念呀～犹记得语文学习小组，每天轮流一人执笔，另一人和老师同时点评，最后每周每组选出一人上去全班朗读。这两篇作文都是这样被推荐的哦！&lt;/p&gt;
&lt;p&gt;:::TIP[《中国，世界》补档（存档）]
836 Words / Monday, September 16, 2019, 9:40:22 PM
:::&lt;/p&gt;
&lt;h1&gt;路&lt;/h1&gt;
&lt;p&gt;跑在江边崭新的跑道上，路经一片片旧址和闹市，穿过一座座大桥小桥，驿站里独具地区特色的那一张张照片，上海中心上若隐若现着那一行行文字，引起我对祖国的鉴赏与骄傲。&lt;/p&gt;
&lt;p&gt;小时候，我的父亲长期被外派于荷兰，我于是也经常前去游玩。听父亲说，这里楼下马上就要建一个火车站(即中国的地铁站)了，外面也将辟上一条公路，以后从机场到鹿特丹只需要一个小时。果不其然，第一次去荷兰就见得楼下几乎已准备就需，开始铺起一层层楼，铲起一片片土。&lt;/p&gt;
&lt;p&gt;而我，当时还住在上海的那老旧的公寓楼里，阴冷潮湿；楼前混乱的垃圾伴着风，有的和着灰尘漫天飞舞，有的躺在地上呻吟，渴望得到归宿。轿车刚刚普及，低碳节能也便只成了口号，人们对小康社会工业效率的追求，又导致了严重的雾霾。夜晚，黯淡的灯光打在小段滨江大道上密集的人群中，原本美好的江景，被絮乱的环境打破了。于是乎，荷兰这样强大、干净、安静、和谐的地方，自然引起我无限的遐想，而对于荷兰那新颖的建筑设计，我也很是羡慕，似乎宛若天仙。&lt;/p&gt;
&lt;p&gt;“爸爸，我们为什么不坐新修的火车回来呀？”暑假里，我又一次来到了荷兰，希望看到那心心念念的火车站。可是，放眼望去，依然保留着第一次去的风貌：一层层楼依然只是不变高度的骨架，一块块马路依然只是一个个没有人填的坑洞。他们也许还在设计吧，我想。&lt;/p&gt;
&lt;p&gt;此时，骤然间，我的念头被远方嗡嗡的电钻声打断。似乎，最近，家里的生活、小区环境都在慢慢变好：一年时间，难得一见的一小盘肉蛋变为了日常的一块块牛肉，一年时间，混乱的架空线入地了，取而代之的是一块块公共绿化，一年时间，断续的一块块滨江大道贯通了，连接着一段段暇意的江路。进博会，人工智能大会，周围长驻外国人越来越多，全没了往日一片冷清一片热闹的模样。远方建起港珠澳大桥，近些筑起上海中心，国家说干就干，总是能出乎预料。&lt;/p&gt;
&lt;p&gt;再看看荷兰。直到现在，那片土地上，依然是一层层不变的骨架，一个个单调的土堆……&lt;/p&gt;
&lt;p&gt;如今，祖国已经壮大，城市变得美丽，经济也已发展，给我的，不仅是条件的优裕，更是晴天的希望，无穷的自豪！&lt;/p&gt;
&lt;p&gt;:::TIP[《忆》补档（存档）]
641 Words / Wednesday, September 5, 2018, 8:09:20 PM
:::&lt;/p&gt;
&lt;h1&gt;村&lt;/h1&gt;
&lt;p&gt;小猪啼，深思忆。&lt;/p&gt;
&lt;p&gt;远远的，历经两个小时的“长途跋涉”，随着好几家的欢声笑语，乘着车，来到了妈妈同学的家乡。&lt;/p&gt;
&lt;p&gt;那个地方的一切对我都是那么新奇，无边的田野，虽矮但不缺情调的小土房，---都是那么令人着迷。。。。。。&lt;/p&gt;
&lt;p&gt;我一下车，就“相中”了那小溪，长长的小溪清澈见底，欢快的鱼儿呢，也在那里嬉戏。我沿着它，悠闲地走着。这令人思念的景色啊！&lt;/p&gt;
&lt;p&gt;据妈妈的同学介绍，这里的水十分适合打水漂。我一听，立马兴奋起来了，我像个小保镖，跟着他学打水漂。拿一个圆圆的鹅卵石，越轻越好，再对准大约45度斜度的那个点，轻轻一扔，又快又准，打到“千里之外去”了。可是我怎么也成功不了！我打后，只溅起一丝水波，就沉没了。&lt;/p&gt;
&lt;p&gt;打了几次，始终学不会，我便不玩了，跑到小溪上的一座铁桥。这铁桥都生锈了，看起来有些年头了，好似满载回忆。在上面飞奔，玩耍，尽情地挥洒着汗水；在上面说说笑笑，追追打打，热情地俯视着溪水。。。。。。一切都是那么有趣。&lt;/p&gt;
&lt;p&gt;“妈妈，看，好可爱的小鸟！”&lt;/p&gt;
&lt;p&gt;“是呀，着水养育着这鸟，这鸟守护着这水，两者不可分离。”妈妈笑盈盈地说。&lt;/p&gt;
&lt;p&gt;不知不觉，我走到了妈妈同学的家。只见一只野猪疯狂地挣扎着，身上的血一滴又一滴地流了出来。原来在杀猪啊！我一看，便跑到妈妈的背后，抱着妈妈，哇哇大哭起来。&lt;/p&gt;
&lt;p&gt;不久，我听说开饭了。见食眼开的我，立马不哭了，跑到大厅，哇！今天有猪肉汤！我立马从汤里夹起了一块肥嫩的猪肉，细细地品尝了起来。&lt;/p&gt;
&lt;p&gt;远处，夕阳西下，猪仔在啼叫，近处，溪水流淌，我们满载欢笑。这里的一切的一切，包括小猪，古桥，溪水，都将荡漾在我心中。&lt;/p&gt;
</content:encoded></item></channel></rss>