<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>技術紀錄 on dw's 小站</title><link>https://dwye.dev/topics/%E6%8A%80%E8%A1%93%E7%B4%80%E9%8C%84/</link><description>Recent content in 技術紀錄 on dw's 小站</description><generator>Hugo -- gohugo.io</generator><language>zh</language><lastBuildDate>Fri, 30 Jan 2026 21:36:06 +0800</lastBuildDate><atom:link href="https://dwye.dev/topics/%E6%8A%80%E8%A1%93%E7%B4%80%E9%8C%84/" rel="self" type="application/rss+xml"/><item><title>下班四個月考過 AWS Solution Architect Associate (SAA-C03) 心得</title><link>https://dwye.dev/post/saa-c03/</link><pubDate>Fri, 30 Jan 2026 21:36:06 +0800</pubDate><guid>https://dwye.dev/post/saa-c03/</guid><description>
&lt;p&gt;&lt;img src="https://dwye.dev/img/saa-c03.png" alt="Preparing SAA-C03"&gt;&lt;/p&gt;
&lt;h2 id="考這個證照的原因"&gt;考這個證照的原因&lt;/h2&gt;
&lt;p&gt;目前在公司的職位會碰到不少 AWS 以及軟體架構設計問題，針對既有的 Legacy 系統提出改善與翻新方案時，我需要能夠提出多種方案的設計，並進行比較，以及找出最佳實踐。而又由於我們公司的軟體基礎設施的遷移方向正逐步轉向以託管服務為主，因此這張證照對我在公司的職位來說十分實用的。&lt;/p&gt;
&lt;p&gt;更重要的是，公司全額贊助這個考試的費用 150 美金以及相關教材費用（包含 Udemy 課程以及模擬試題，稍後會列出），我只需要負責專心唸書跟考試就好。綜合各方面考量下，實在沒什麼不考的理由。&lt;/p&gt;
&lt;h3 id="我的背景"&gt;我的背景&lt;/h3&gt;
&lt;p&gt;我雖是台大畢業，但我非資工本科，轉職軟體工程師工作五年多，近四年工作以 Platform Engineer 為主，組織架構上隸屬 Infrastructure Team，是負責維護共用服務的工程師，工作上比較少直接參與 AWS Infrastructure 的架設，多半還是作為使用者的角色居多。&lt;/p&gt;
&lt;p&gt;準備考試的難易程度因人而異，你可以依據這些背景判斷我分享的經驗對你有多大參考價值。&lt;/p&gt;
&lt;h2 id="考試準備"&gt;考試準備&lt;/h2&gt;
&lt;p&gt;先列一下時程表，我大概是 2025.10 月開始準備，然後 2026.01 考試，所以是四個月準備時間，前面兩個半月看完課程後，就不斷寫模擬試題並檢討為主。&lt;/p&gt;
&lt;h3 id="課程素材"&gt;課程素材&lt;/h3&gt;
&lt;p&gt;我採用的準備教材是許多人十分推薦的 Udemy 課程── Stephane Maarek 的「&lt;a href="https://www.udemy.com/course/aws-certified-solutions-architect-associate-saa-c03"&gt;Ultimate AWS Certified Solutions Architect Associate 2025&lt;/a&gt;」課程。一方面是公司內資深的同事也是推薦相同的課程，另一方面我認為有一個影音課程能讓我先把考試範圍輕鬆的順過一次並且拿來配飯吃，也會減輕我準備時的難度，所以就直接選了這門課。我上完課的心得，也是十分推薦這門課。這門課有 Lab 環節可以跟著理解如何一步一步設置這些 AWS 服務，觀念的部分也是簡明扼要，講的十分易於理解，基本上不太需要我額外去閱讀或是搜尋其他補充資料，並且涵蓋了蠻大一部分的考試內容，足夠讓你我通過考試了。課程本身也會與時俱進，對於 AWS 新推出的 Service 或更動的功能，也會在課程中補充更新。像是我發這篇文的現在，課程標題也從 2025 更新到 2026 了 XD。&lt;/p&gt;
&lt;p&gt;至於學習的細節，我通常是開倍速（1.5x 到 1.75x）看課程，而我在看完課程後，會根據服務分類，在 Notion 做筆記整理重點，也方便我後續複習，因為複習的時候看文字比看影片快多了，我也能隨時在自己的筆記內加入我想補充的內容。&lt;/p&gt;
&lt;h3 id="模擬試題"&gt;模擬試題&lt;/h3&gt;
&lt;p&gt;閉門造車是不夠的，總是要試試身手，也因此需要對應的模擬試題。&lt;/p&gt;
&lt;p&gt;也因此，我也買了配套的考試──同樣是 Stephane Maarek 的「&lt;a href="https://www.udemy.com/course/practice-exams-aws-certified-solutions-architect-associate"&gt;Prepare for your SAA-C03 exam. 390 high-quality practice test questions written from scratch with detailed explanations!&lt;/a&gt;」。我使用這份試題的方式，基本上就是開啟計時模式，當作真正的考試在寫：過程不看筆記、不查資料。有時，我不一定會用完整的兩小時時段來寫這份題目，我會利用零碎的時間寫，反正考試可以暫停，就自己記得中間不要去偷查資料就好。一般來說，一回題目我會花一小時半左右完成，然後再配著詳解檢討，並回頭訂正以及加強我的筆記。如果有看了詳解還是不懂的地方，丟給 Gemini 3 Pro，基本上都能講到看懂。&lt;/p&gt;
&lt;p&gt;不過，相較於 Stephane 的課程本身，這份模擬考我就沒有很推薦了，主要原因有三：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第一，是難度因素，題目有不少題考的十分刁鑽，會有即便是你看了解答還是覺得有些模稜兩可的題目，甚至連 Gemini 都會答錯的等級。我寫這份試題的答對率落在 70%-80% 之間，算是在及格邊緣。這種難度雖然也是能幫助複習，可是考題本身我也並不那麼喜歡，大多題目偏長，需要較久的時間在腦中整理圖像以及作答。&lt;/li&gt;
&lt;li&gt;第二，是題目包含有些過時內容，包含較舊的名詞（例如 Kinesis Data Analytics，現在已經改稱 Managed Service for Apache Flink）、和較舊的機制（Launch Configuration、Origin Access Identity 等等）。&lt;/li&gt;
&lt;li&gt;第三，是題目的重複性，最嚴重的是 Stephane 的課程本身就包含了一些模擬題目，而這些模擬題目會在這份模擬試題中打散重新出現，這樣對於模擬測驗的意義就打折扣了，這是我覺得十分需要避雷的一點。此外，同一回的題目也會有部分換句話說考類似的東西，有點像是同一題寫了兩次，我也不喜歡這樣。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;那麼替代方案呢？我看比較多人推薦 Tutorial Dojos 的模擬試題──「&lt;a href="https://portal.tutorialsdojo.com/courses/aws-certified-solutions-architect-associate-practice-exams/"&gt;AWS Certified Solutions Architect Associate SAA-C03 Practice Exams 2026&lt;/a&gt;」，同樣是 15 USD 左右的價格，這一份有九組模擬試題，題目涵蓋也似乎比較廣，至於實際做起來的感覺如何呢？就留給接下來考試的各位來體驗吧。&lt;/p&gt;
&lt;p&gt;此外，AWS 官方的 Skill Builder 的培訓「Official Practice Question Set: AWS Certified Solutions Architect - Associate」也有 20 題的的免費試題可以體驗，我十分推薦考前先體驗一下官方試題的出題感覺跟難度。&lt;/p&gt;
&lt;h3 id="若你來不及準備"&gt;若你來不及準備&lt;/h3&gt;
&lt;p&gt;其實考前 24 小時內可以免費取消或改期。我原本訂的是 11 月，但一個月準備時間實在不太夠，於是決定直接延期到一月，給自己充足的時間再應考。&lt;/p&gt;
&lt;h3 id="esl30"&gt;ESL+30&lt;/h3&gt;
&lt;p&gt;對於非英語母語考者，如果選擇以英語考試，可以在報名前申請「ESL+30」(English as a Second Language + 30 minutes)，延長 30 分鍾考試時間。測驗是可以提早交卷的，只有好處沒有壞處，那就是一律申請起來。&lt;/p&gt;
&lt;p&gt;如果你跟我一樣，報名的時候忘了先申請 ESL+30，這時候申請的話，是無法直接套用到已經報名的考試的，所以解決方法就是：免費取消，重新報名。（所以我總計取消了一次，改期了一次。）&lt;/p&gt;
&lt;h2 id="考試當天"&gt;考試當天&lt;/h2&gt;
&lt;p&gt;能選的考場不多，我能到達的都在台北市，汐止跟基隆完全沒有任何考場，對於北海岸人相對不友善。而且時間選擇也有限，蠻多考場都只開平日，所以我就直接挑了一天下午請了特休，悠閒地睡飽考試去了。&lt;/p&gt;
&lt;p&gt;考前問了 Perplexity 整理出攜帶清單，基本上只有一樣必帶一樣推薦帶，我覺得都十分實用，因此我也推薦給大家：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;必帶：雙證件，要有一張有英文，這個選擇可以是有英文名的信用卡或護照，為了簡單，我直接帶了護照去。&lt;/li&gt;
&lt;li&gt;推薦帶：外套，考場冷氣真的蠻強的，即便我在冬天考，在考場裡面還是坐到會冷。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;官方給的行前提醒是建議提早 15 分鐘抵達考場，而我為了謹慎，提前了半小時抵達考場，結果一兩分鐘就完成報到了 XD。意外的是，櫃檯的人就直接問我要不要先進去考試，剛好考場還有空位，原來這是隨到隨考的，我想了想去上了廁所就進去考試了。&lt;/p&gt;
&lt;h3 id="進入考場"&gt;進入考場&lt;/h3&gt;
&lt;p&gt;在寄物完後，考場人員會檢查你身上沒有任何東西，包含手錶也是要拿下來的，基本上只有人可以進去，你能依賴的就只有考場電腦上的時鐘——就什麼都別想，專心你的考試吧。&lt;/p&gt;
&lt;p&gt;我原先以為會是一個像電腦教室一樣的地方，一堆人被關在一起考試，但其實考試中心的考場是一間一間超級小房間，大概就 K 書中心的座位大小，然後放一套電腦跟座椅以及紙筆。電腦螢幕設備都是最普通的，沒有滑鼠墊，有時候會滑不準，椅子也不好坐，如果平常在家用太好的設備（像我）可能就會有點不習慣 XD。進去後會先閱讀一些規定，然後可以調整試題主題配色，我調成黑底白字，這樣比較工程師友善，他還有一些奇怪的配色可以選，例如藍底白字，走一個 Windows 故障風格，但看久了眼睛大概會很累 XD。考試是全程錄影的，頭頂有個攝影機，應該是為了防止作弊，你可以舉手說要去上廁所，不過考試時間也不算長，我就直接一次寫完走人，包含檢查大概花了 1 小時 40 分鐘左右。&lt;/p&gt;
&lt;h3 id="考試結果"&gt;考試結果&lt;/h3&gt;
&lt;p&gt;當天晚上九點，就收到了 Credly 寄來我的勳章，也就代表了我的考試正式通過了，距離我交卷也才 4 小時，算很有效率。隔天早上起床，就收到半夜寄來的成績單，最終如同考試時的手感，拿到了 823 分，微微高空飛過，算是滿意的結果。不過成績單並沒有除了分數之外的有意義資訊，不會告訴你對了幾題錯了幾題，而非整數的分數，大概也是因為每題的分數不是相同的。&lt;/p&gt;
&lt;h2 id="綜合心得"&gt;綜合心得&lt;/h2&gt;
&lt;p&gt;正式考試的題目整體來說比 Stephane 的模擬試題簡單不少，不過也包含一些 Stephane 的課程沒有覆蓋的內容（基於保密條款，我無法舉例）。我覺得這次考試學習最大的感觸就是： AI 真的幫助我學習很多。過去遇到不懂的東西，都要上網搜尋讀文件研究許久，現在直接扔給 Gemini 3 Pro 就可以 一次對話給出好懂的解釋，若想要深入了解更多，追問之下也能給出滿意的答案。我可以根據這些回答，整理回我的筆記，讓我的筆記更加有參考價值。&lt;/p&gt;
&lt;p&gt;也不禁感嘆，若是我當學生的時候就有這些 AI 模型，或許整體的學習氛圍會變得完全不一樣，願意學習的人都可以更自主學習，更少依賴教師、補習班、班上的學霸等等；但也可能因此我大學時就沒這麼容易找到解題老師以及家教的打工機會了。&lt;/p&gt;
&lt;p&gt;最後是考試的準備心得，即便平時工作就會用到這些考試的東西，但沒有使用到的 AWS 服務也是不少，例如關於 Data Pipeline / Streaming / Analytics、以及 ECS 家族的服務我幾乎沒有在使用，也因此我還是花了不少時間學習補強。準備期間，雖説不會到每天都在唸書的程度，卻也是佔用了一週約 10 小左右的下班時間，也有幾個假日是完全整天都在唸書的。如果要再考下一張證照，我會想要先放鬆一下，還給自己一些下班休閒時間。&lt;/p&gt;
&lt;p&gt;總結來說，我十分推薦考這張 AWS Solution Architect Associate (SAA-C03) 證照，藉由這次考試準備，讓我更加了解到不少最佳實踐的例子以及常見的 anti-pattern，相信我回到工作本業的時候，更能夠給出好的設計，也能夠避免不小心採用了增加技術債的做法。&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>Raycast - 集大成的 Mac 生產力工具</title><link>https://dwye.dev/post/raycast-mac-productivity/</link><pubDate>Mon, 18 Sep 2023 23:59:45 +0800</pubDate><guid>https://dwye.dev/post/raycast-mac-productivity/</guid><description>
&lt;p&gt;&lt;a href="https://www.raycast.com/"&gt;Raycast&lt;/a&gt; 是最近 MacOS 上生產力工具的新星，是 Spotlight 的加強版，不只可以像 Spotlight 一樣作為啟動器，開啟各種 App 和網頁搜尋，更能透過各種熱鍵和擴充幫助我們平常使用 Mac 時省下很多時間，是我非常推薦的一款必裝生產力工具。透過各種方便的擴充，能加速我們的工作流程，省下很多時間，提升生產效率。另一個優點是，Raycast 的介面簡單優雅，看了心情就好！&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/raycast/cover.jpeg" alt="Raycast and Logo"&gt;&lt;/p&gt;
&lt;h2 id="tldr"&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;先下載來試試吧，你唯一後悔的會是沒有早點開始使用 Raycast：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.raycast.com/"&gt;https://www.raycast.com/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="raycast-讓人愛不釋手的理由"&gt;Raycast 讓人愛不釋手的理由&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;更快速的 Spotlight&lt;/li&gt;
&lt;li&gt;簡潔好看的介面&lt;/li&gt;
&lt;li&gt;大量方便的內建指令&lt;/li&gt;
&lt;li&gt;大量的社群 Extensions&lt;/li&gt;
&lt;li&gt;支援各種自定義熱鍵以及 Alias&lt;/li&gt;
&lt;li&gt;方便的 Menu Bar 小工具&lt;/li&gt;
&lt;li&gt;Snippets 幫你記住常用的字詞片段（像是地址，Email 發票載具等）&lt;/li&gt;
&lt;li&gt;剪貼簿歷史（Clipboard History），可以搜尋過去複製過的東西&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;上面這些都是免費版就能做到的功能！而升級 Pro 版本可以快速存取 AI，透過各式 AI 指令讓 Raycast 變得更強大。&lt;/p&gt;
&lt;h2 id="為什麼說-raycast-是生產力工具的-game-changer-呢"&gt;為什麼說 Raycast 是生產力工具的 Game Changer 呢？&lt;/h2&gt;
&lt;p&gt;以前總是會有很多 Youtube Video 介紹各種 Mac 上的生產力工具，包含視窗管理、App 管理、剪貼簿管理、快速計算機等等各種小工具，也有像 Alfred 那樣取代 Spotlight 的啟動器。雖然乍看 Raycast 好像又是另一個 Alfred 替代品，但其實 Raycast 更有野心： Raycast 的目標就是要做到 All in one，讓我們只需要 Raycast 單一一個工具，讓 Raycast 成為真正的快速啟動器，啟動各種你我生活及工作中會使用到的 Workflows。即便 Raycast 還很年輕（2020 Public Beta），就已經有如此多功能，甚至還在進化當中，常常我打開 Raycast，就會看到提示說又多了哪些新功能。官方也有 Slack 社群讓使用者提供 Feedback。&lt;/p&gt;
&lt;h3 id="是誰創造了-raycast"&gt;是誰創造了 Raycast&lt;/h3&gt;
&lt;p&gt;Raycast 的背後是一家同名公司，CEO 是 Thomas Paul Mann，CTO 是 Petr Nikolaev，根據經歷都是英國 Facebook / Meta 出來的工程師，可以透過 Google 搜尋找到他們的 LinkedIn 頁面，而他們的 Career 頁面也有完整的員工名單，目前已經擴展至 21 名員工，而且也有在招募中，是全遠端工作的公司，不過需要以歐洲時區為主，可以前往他們的 Career 頁面了解相關資訊：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.raycast.com/careers"&gt;https://www.raycast.com/careers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;好了，前言已經夠長，話不多說，讓我們從最基本功能的開始說起。&lt;/p&gt;
&lt;h2 id="取代-spotlight-的快速啟動器"&gt;取代 Spotlight 的快速啟動器&lt;/h2&gt;
&lt;p&gt;Spotlight 就是平常我們按 Cmd + Space 叫出的那個：&lt;br&gt;
&lt;img src="https://dwye.dev/img/raycast/spotlight.jpeg" alt="Spotlight Example"&gt;&lt;/p&gt;
&lt;p&gt;在使用 Raycast 之前，我使用 Spotlight 的情境，基本上就是兩種：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;搜尋檔案&lt;/li&gt;
&lt;li&gt;打開特定 App&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不過 Spotlight 在這部分有明顯的延遲感，輸入之後必須要等待 0.X 秒才會顯示出結果，相較之下，Raycast 就可以幾乎即時的顯示出搜尋結果。還能夠設定喜愛項目，直接快速存取：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/raycast/favorite.jpeg" alt="Raycast Favorite"&gt;&lt;/p&gt;
&lt;p&gt;這時候輸入 Cmd + 數字，就能直接選擇對應的指令，十分方便。&lt;/p&gt;
&lt;h3 id="存取系統設定"&gt;存取系統設定&lt;/h3&gt;
&lt;p&gt;有時候想更改系統設定，卻總是要找半天？ Raycast 可以直接搜尋相關的系統設定項目，就能點開對應的設定，例如我想設置顯示相關的東西，就搜尋 &amp;ldquo;Display&amp;rdquo;：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/raycast/display.jpeg" alt="Raycast System Settings: Display"&gt;&lt;/p&gt;
&lt;h3 id="系統指令"&gt;系統指令&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;如果我有外接硬碟，可以透過 &lt;code&gt;Eject All Disks&lt;/code&gt; 把他們全部退出。&lt;/li&gt;
&lt;li&gt;可以透過 &lt;code&gt;Empty Trash&lt;/code&gt; 清理垃圾桶。&lt;/li&gt;
&lt;li&gt;可以用 &lt;code&gt;Lock Screen&lt;/code&gt; 快速鎖住電腦。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;之類的方便的系統指令還有很多！&lt;/p&gt;
&lt;h2 id="使用熱鍵"&gt;使用熱鍵&lt;/h2&gt;
&lt;p&gt;Raycast 的所有指令都支援熱鍵設置，像我自己把 Opt + Space 設置成搜尋 App 的選單，讓我快速存取一些沒有熱鍵的常用功能，像是複製現在的 Tab：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/raycast/hotkey.jpeg" alt="Raycast Hotkey"&gt;&lt;/p&gt;
&lt;p&gt;而最近的 Raycast CEO 訪談中，也示範了使用熱鍵開啟特定的 App：例如用 Opt + N 打開 Opt + A 打開 Arc 瀏覽器等等。詳細可以看這部影片：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://youtu.be/xRnMXJcH9Pg?si=3Ru-eJURenxLjyey&amp;amp;t=108"&gt;https://youtu.be/xRnMXJcH9Pg?si=3Ru-eJURenxLjyey&amp;t=108&lt;/a&gt; （影片 108 秒處）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;（不過我自己是覺得叫出 Raycast 搜尋 App 就足夠快速了）&lt;/p&gt;
&lt;h3 id="幫指令取別名縮寫alias"&gt;幫指令取別名縮寫（Alias）&lt;/h3&gt;
&lt;p&gt;這個我就很常用了。第一個使用情境是為 App 取他的縮寫名稱，例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;tg&lt;/code&gt; for Telegram&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dc&lt;/code&gt; for Discord&lt;/li&gt;
&lt;li&gt;&lt;code&gt;no&lt;/code&gt; for Notion&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nt&lt;/code&gt; for Note (Apple Note)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;java&lt;/code&gt; for IntelliJ（抱歉我永遠記不得這個名字）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;當然，不只是開啟 App，其他指令以及擴充都能取 Alias，例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;spp&lt;/code&gt; Toggle Play Spotify，特別是當目前剛暫停別的東西（例如 Youtube 影片）而想要播放 Spotify 時，我只需要叫出 Raycast 輸入 &lt;code&gt;spp&lt;/code&gt; 就能播放 / 暫停 Spotify 了，而不用像原生的播放鍵，會播放你上一個播放的東西（也就是 Youtube 影片）&lt;/li&gt;
&lt;li&gt;同理 &lt;code&gt;svv&lt;/code&gt;，&lt;code&gt;svd&lt;/code&gt; 可以單獨調整 Spotify App 的音量&lt;/li&gt;
&lt;li&gt;&lt;code&gt;j&lt;/code&gt; + query 搜尋 Jira Tickets&lt;/li&gt;
&lt;li&gt;&lt;code&gt;g&lt;/code&gt; + query 可以使用 Google 搜尋&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/raycast/query.jpeg" alt="Raycast Google Search with Query"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ud&lt;/code&gt; 可以搜尋 Urban Dictionary，一個很有趣很道地的字典，常常會有一些梗在裡面&lt;/li&gt;
&lt;li&gt;&lt;code&gt;code&lt;/code&gt; 使用 VSCode 搜尋專案，是我工作中最常使用的指令之一&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="快速進入視訊會議"&gt;快速進入視訊會議&lt;/h2&gt;
&lt;p&gt;除了原本 Spotlight 的功能外，Raycast 更提供了大量的指令與好用的功能。&lt;/p&gt;
&lt;p&gt;例如同步行事曆後，如果你接下來有 Meeting，就會顯示在 Raycast 視窗下方，如果有設定視訊會議連結，就可以按下 Enter / Return 一鍵加入！我幾乎每場公司的會議都是用這個功能加入的。（缺點就是因為太方便了，我常常成為最早加入的那個人 XD）&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/raycast/meeting.jpeg" alt="Enter Meeting in Raycast"&gt;&lt;/p&gt;
&lt;h2 id="萬用計算機"&gt;萬用計算機&lt;/h2&gt;
&lt;p&gt;匯率，時區，或是簡單的數值計算，直接打到 Raycast 就會有結果了。&lt;/p&gt;
&lt;p&gt;例如我常常和美西時區的同事合作，需要知道他們的時間是幾點，就可以用這個功能：&lt;br&gt;
&lt;img src="https://dwye.dev/img/raycast/pdt.jpeg" alt="Timezone Convert Raycast"&gt;&lt;/p&gt;
&lt;p&gt;或是上京東看了個喜歡的產品，想知道台幣多少錢：&lt;br&gt;
&lt;img src="https://dwye.dev/img/raycast/rmb.jpeg" alt="RMB TWD Convert Raycast"&gt;&lt;/p&gt;
&lt;p&gt;算完的結果還能透過 Enter 直接貼上到目前的應用程式。&lt;/p&gt;
&lt;h2 id="menu-bar-小工具"&gt;Menu Bar 小工具&lt;/h2&gt;
&lt;p&gt;Raycast 提供了方便的 API 可以讓他的擴充套件有 Menu Bar 顯示功能，這也是 Raycast 我最愛的功能之一。這裡展示了我的 Raycast 相關的 Menu Bar 設置：&lt;br&gt;
&lt;img src="https://dwye.dev/img/raycast/menubar.jpg" alt="Raycast Menubar"&gt;&lt;/p&gt;
&lt;p&gt;例如，我同步了行事曆，他就可以隨時顯示我的下一個行程在什麼時候。&lt;/p&gt;
&lt;p&gt;我也設置了 Github Notification 和 Pull Request 的顯示。以 Github Notification 為例，當有人開 PR 讓我 Review 或是有人 Review 我的 PR 時，我就能看到有通知，而我通常會把事情做一個段落才會一次性的看現在有哪些 GitHub 通知，就不會隨時被擾人的通知打擾。另一個是我的 Pull Request 數量，我可以隨時觀看我還有哪些 Work in Progress 的 Pull Request，讓我了解到自己還有哪些 Code 可能還沒合進主要分支。&lt;/p&gt;
&lt;p&gt;此外，我自己是 Spotify 重度使用者，電腦幾乎隨時都播著音樂，有時候如果播放 Spotify 推薦的歌單，這個 Menu Bar 顯示可以讓我隨時知道現在播的是什麼歌。&lt;/p&gt;
&lt;p&gt;這邊分享一個小祕技，可以用 Cmd + 滑鼠拖移 Menu Bar 裡面的 Item，把他們放到喜歡的位置。這樣就不用怕 Menu Bar 太多東西了，可以把不喜歡的東西往左邊丟，或是也能用 &lt;a href="https://github.com/dwarvesf/hidden"&gt;Hidden Bar&lt;/a&gt; 這個 Menu Bar 管理工具來隱藏不需要的 Items。&lt;/p&gt;
&lt;h2 id="快速連結-quicklink"&gt;快速連結 Quicklink&lt;/h2&gt;
&lt;p&gt;簡單來說，就是快速打開網頁連結，有點像是瀏覽器的書籤，例如用這個連結可以快速點開 Gmail 頁面：&lt;br&gt;
&lt;img src="https://dwye.dev/img/raycast/quicklink_gmail.jpeg" alt="Raycast Quicklink Gmail"&gt;&lt;/p&gt;
&lt;p&gt;更進階的用法是，可以輸入參數，讓連結客製化，例如這個開啟 Jira Issue 的 Quicklink：&lt;br&gt;
&lt;img src="https://dwye.dev/img/raycast/quicklink_argument.jpeg" alt="Raycast Quicklink Jira Issue with Argument"&gt;&lt;/p&gt;
&lt;p&gt;接下來為該指令設置一個簡單的 Alias &lt;code&gt;j&lt;/code&gt;，我就可以透過指令快速開幾那個 Issue 的網頁：&lt;br&gt;
&lt;img src="https://dwye.dev/img/raycast/quicklink_jira_example.jpeg" alt="Raycast Quicklink Opening Jira Issue Example"&gt;&lt;/p&gt;
&lt;p&gt;這和 Jira Extension 提供的功能似乎很像，不過這個功能我可以直接開啟網頁，不需要先在 Raycast 開啟 Jira Search Issue 指令搜尋那個 Issue 才能打開網頁，對於常用的流程，少一步是一步。&lt;/p&gt;
&lt;h2 id="snippets"&gt;Snippets&lt;/h2&gt;
&lt;p&gt;Snippets 的中文是小片段的意思，而在 Raycast 中，Snippets 功能讓我們可以快速的產生一些常用的文字片段，像是一些永遠記不住 Commands：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/raycast/snippets.jpeg" alt="Raycast Snippets Git Submodules"&gt;&lt;/p&gt;
&lt;p&gt;我常用的 Snippets 還有中英文地址，Email 和發票載具等常用的字串，並且可以透過設置 Keyword，當你打出特定的 Keyword 時，Raycast 就會自動幫你取代成 Snippets 的內容，像是我把 &lt;code&gt;;;env&lt;/code&gt; 設置成電子發票載具，就不用每次都手動輸入。&lt;/p&gt;
&lt;p&gt;更詳細的用法可以看介紹：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.raycast.com/extensions/snippets"&gt;https://www.raycast.com/extensions/snippets&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以及官方 Demo：&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/gSbZjxgl1Qc?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;附帶一提，這個 Quick Tip 系列都蠻值得一看的，我有追蹤 Raycast 的 YouTube，他們每部影片我都能從中得到一些有用的 Raycast 小知識。&lt;/p&gt;
&lt;h2 id="剪貼簿歷史-clipboard-history"&gt;剪貼簿歷史 Clipboard History&lt;/h2&gt;
&lt;p&gt;Clipboard History 也是常常被生產力工具所強調的功能之一，Raycast 當然也具備了這個功能，可以讓我們搜尋過去的複製紀錄，並且按 Enter 直接貼上，或 Cmd + Enter 複製他，連圖片或檔案都會在歷史中，真的十分方便！&lt;/p&gt;
&lt;p&gt;這個也有官方介紹，歷史是以加密的方式存在本地的，並且會忽略從密碼管理器中複製的項目：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.raycast.com/extensions/clipboard-history"&gt;https://www.raycast.com/extensions/clipboard-history&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="刪除不需要的-app"&gt;刪除不需要的 App&lt;/h2&gt;
&lt;p&gt;以前我都是用 AppCleaner 來反安裝一些沒有自帶反安裝功能的 App，現在 Raycast 也實現了這個功能。只要在 App 按下 Cmd + k 會顯示很多不同的 Action，裡面其中一個就是 Uninstall Application，而且別擔心他不會一點下去就刪除，會先讓你預覽要刪除的檔案有哪些，可以全程透過鍵盤操作，和 AppCleaner 比起來方便許多：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/raycast/uninstall.jpeg" alt="Raycast Uninstall Demo"&gt;&lt;/p&gt;
&lt;h2 id="豐富的擴充功能"&gt;豐富的擴充功能&lt;/h2&gt;
&lt;p&gt;其實在上面已經多少有提到，Raycast 支援各種擴充功能，可以直接在 Raycast 內透過 &lt;code&gt;Store&lt;/code&gt; 搜尋，或是直接上網找：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.raycast.com/store"&gt;https://www.raycast.com/store&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一些我很常用的 Extensions 如下（有些是官方，有些是第三方）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Spotify&lt;/li&gt;
&lt;li&gt;Jira&lt;/li&gt;
&lt;li&gt;GitHib&lt;/li&gt;
&lt;li&gt;VSCode&lt;/li&gt;
&lt;li&gt;Define 查字典&lt;/li&gt;
&lt;li&gt;Image Conversion: 寫這篇文章的截圖都是用這個擴充轉檔，把 PNG 轉成 JEPG 來減小大小&lt;/li&gt;
&lt;li&gt;Jira&lt;/li&gt;
&lt;li&gt;Audio Device 切換聲音輸出&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;還有兩個常用的生產力功能 Extension 也十分推薦，不過我還是習慣目前的工具，就沒有用 Raycast 取代他們：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Navigation（我使用 &lt;a href="https://github.com/lwouis/alt-tab-macos"&gt;AltTab&lt;/a&gt;，可以覆蓋原本的 Cmd + Tab 熱鍵，這個 Raycast 目前還做不到）&lt;/li&gt;
&lt;li&gt;Windows Management（我使用 &lt;a href="https://apps.apple.com/tw/app/magnet/id441258766"&gt;Magnet&lt;/a&gt;，比 Raycast 多支援了滑鼠拖拉）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;當然還有更多 Extensions，我也還在持續探索中，如果讀者有發現推薦的 Extension 也歡迎和我分享。&lt;/p&gt;
&lt;h2 id="這是免費能用的嗎"&gt;這是免費能用的嗎？&lt;/h2&gt;
&lt;p&gt;是的，即便有了這麼多厲害的功能，Raycast 對於個人使用是免費的。而根據&lt;a href="https://www.raycast.com/faq"&gt;官方的 FAQ&lt;/a&gt;，他們也希望維持個人使用是免費的。當然他們也有提供付費選項，目前 Pro 是以月費 10 USD / 年繳每月 8 USD 的價格，開放使用 AI 相關功能，雖然 AI 其實就是串接 OpenAI 的 API，當然也可以直接上 OpenAI 網站免費使用，不過 Raycast AI 可以客製化指令，並透過 Raycast 快速存取，比起打開網頁輸入落落長的問題實在是方便多了，如果要談論如何好好善用 Raycast 的 AI 功能可以再寫一篇文章，這方面先就交由官方影片來解釋吧：&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/46As11PMJCU?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;基於對 Raycast 的支持，我有付費使用，也是因為我真的蠻喜歡這個工具，同時我也是重度使用者，所以我才寫了這篇文章推廣。&lt;del&gt;希望以後 Raycast 也能像 Notion 那樣推出推薦制度，讓我加減賺一點回報哈哈哈。&lt;/del&gt;（Raycast 在 2023/11/15 推出推薦制度了！）&lt;/p&gt;
&lt;p&gt;最後再來一點延伸閱讀。&lt;/p&gt;
&lt;p&gt;我十分推薦官方的 Quick Tip 系列，可以了解如何更好的運用 Raycast：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/playlist?list=PLNzQX4H1-dVTHCLKFjekFwswKDpcEN2rI"&gt;https://www.youtube.com/playlist?list=PLNzQX4H1-dVTHCLKFjekFwswKDpcEN2rI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;更多詳細的功能介紹可以參考官方的手冊：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://manual.raycast.com/"&gt;https://manual.raycast.com/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="推薦連結referral-links"&gt;推薦連結（Referral Links）&lt;/h2&gt;
&lt;p&gt;推薦連結（Referral Links）是先搶先贏的，如果你覺得我的文章對你有幫助，歡迎使用我的推薦連結，透過我的連結註冊（不需要付費訂閱 Pro），就可以直接獲得一個月的 Raycast Pro 試用，直接體驗 Quick AI 的和 AI Commands 的強大威力：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.raycast.com/hey/d97fcb56"&gt;https://www.raycast.com/hey/d97fcb56&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.raycast.com/hey/7462d0d3"&gt;https://www.raycast.com/hey/7462d0d3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;自從升級 Pro 之後我成為了 Raycast AI 的重度使用者，有空會再另外寫一篇分享 Raycast Pro 如何在日常生活以及工作中幫助到我。&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>FTX 交易所倒閉的自我檢討與對策</title><link>https://dwye.dev/post/ftx/</link><pubDate>Sat, 12 Nov 2022 20:01:56 +0800</pubDate><guid>https://dwye.dev/post/ftx/</guid><description>
&lt;p&gt;&lt;img src="https://dwye.dev/img/crypto/ftx.png" alt="FTX Logo"&gt;&lt;/p&gt;
&lt;p&gt;熊市來臨。我平常以 FTX 作為主要交易所，以及賺取利息，信仰著 SBF 能帶我們飛，沒想到 SBF 卻是帶我們下地獄。這次倒閉事件我損失了幾乎所有我的加密貨幣，超過 1/3 的總資產，只因為慢了一步逃跑，可以說是 FTX 受災大戶了，我的朋友內還沒有人跟我丟掉一樣多錢的。&lt;/p&gt;
&lt;p&gt;也因此，我決定利用這個假日，遠離一切負面消息，給自己一個時間好好整理來龍去脈，評估損失，以及將來的對策。&lt;/p&gt;
&lt;p&gt;這篇文章不再另外介紹事件的前因後果，如果有興趣，可以查看一些相關報導，例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.blocktempo.com/ftx-liquidity-crisis-and-binance-acquisition-timeline-arrangement/"&gt;BlockTempo - FTX暴雷懶人包》時間軸整理：CZ要砸光FTT 、3天抽光60億鎂，到幣安收購…&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cw.com.tw/article/5123529"&gt;天下雜誌 - FTX瀕臨破產、創辦人身價億萬變1美元　「幣圈巴菲特」SBF怎麼走到這一步？&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="postmortem"&gt;Postmortem&lt;/h2&gt;
&lt;p&gt;軟體業中，若是有事故發生，我們一般會寫個**驗屍報告（Postmortem）**來分析事故，紀錄來龍去脈，以及讓相關人員和公司其他員工能夠從錯誤中學習。&lt;/p&gt;
&lt;p&gt;這次，我把這套方法套用在賠了大量資產後自我檢討。&lt;/p&gt;
&lt;p&gt;這邊格式參考了 &lt;a href="https://www.atlassian.com/incident-management/postmortem/templates"&gt;Atlassian 的模板&lt;/a&gt;。&lt;/p&gt;
&lt;h1 id="postmortem-ftx-倒閉事件"&gt;[Postmortem] FTX 倒閉事件&lt;/h1&gt;
&lt;p&gt;(部分個人資產數字已挖空)&lt;/p&gt;
&lt;h3 id="summary"&gt;Summary&lt;/h3&gt;
&lt;p&gt;FTX 交易所和關聯機構 Alameda 被爆料資不抵債，幣安執行長 CZ 發文說要拋售 FTT，造成用戶恐慌擠兌，SBF 掩蓋真相，最終 FTX 倒閉&lt;/p&gt;
&lt;h3 id="timeline-utc8-2022"&gt;Timeline (UTC+8, 2022)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;11/2 (Wed) 23:44&lt;/code&gt; CoinDesk 發表 Alameda 資不抵債醜聞&lt;/li&gt;
&lt;li&gt;&lt;code&gt;11/6 (Sun) 22:32&lt;/code&gt; Alameda CEO Caroline 反駁醜聞，說那只是一部分&lt;/li&gt;
&lt;li&gt;&lt;code&gt;11/6 (Sun) 23:47&lt;/code&gt; &lt;strong&gt;CZ 發推表示基於風險考量，決定出清 FTT，FTT 開始小跌，FTX 開始有提款潮&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;11/7 (Mon) 00:03&lt;/code&gt; Caroline 說願意和 CZ 以 22 塊收購全部 FTT，給予市場信心&lt;/li&gt;
&lt;li&gt;&lt;code&gt;11/7 (Mon) 01:38&lt;/code&gt; SBF 表示 FTX 能夠負擔客戶資產&lt;/li&gt;
&lt;li&gt;&lt;code&gt;11/8 (Tue) 10:4X&lt;/code&gt; FTT 跌破 22 防線至 15 附近&lt;/li&gt;
&lt;li&gt;&lt;code&gt;11/8 (Tue) 14:20&lt;/code&gt; PTT sbob 回文表示快逃，我看到文章後選擇反向做多 FTT 並觀望，這時已傳出提幣減慢 &lt;strong&gt;（最後逃跑點）&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;11/8 (Tue) 17:XX&lt;/code&gt; FTX 開始傳出停止提幣&lt;/li&gt;
&lt;li&gt;&lt;code&gt;11/8 (Tue) 23:36&lt;/code&gt; &lt;strong&gt;嘗試提幣&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;11/9 (Wed) 00:03&lt;/code&gt; SBF 和 CZ 公告 Binance 收購 FTX 計畫，承認流動性問題，此後已無法提幣。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="impact"&gt;Impact&lt;/h3&gt;
&lt;p&gt;損失 FTX 上資產：________ USD，共約 _____ 萬台幣&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;________ (Blockfolio)&lt;/li&gt;
&lt;li&gt;________ (FTX Pro)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以雷曼事件為類比，好的情況可以拿回三成約 _____ 萬台幣&lt;br&gt;
最壞情況為 _____ 萬台幣歸零&lt;/p&gt;
&lt;h3 id="backlog-check"&gt;Backlog Check&lt;/h3&gt;
&lt;p&gt;事後觀察：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;所有爆料都是真的&lt;/li&gt;
&lt;li&gt;SBF 和 Caroline 在說謊拖延時間&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;認知偏誤：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;相信 FTX 和 Luna 不一樣&lt;/li&gt;
&lt;li&gt;相信 SBF &amp;gt; CZ，假設 SBF 沒有說謊，因此沒有察覺到風險&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;風險管理：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;有接受到消息，卻沒有正確作為：做多 FTT 賺了小段，卻丟了本金，來不及趕上最後逃跑點&lt;/li&gt;
&lt;li&gt;「先跑再說」很明顯是個低成本降低大量風險的行為，但卻主觀認為 FTX 不會倒，所以沒有採取行動&lt;/li&gt;
&lt;li&gt;我的認知是幣圈僅佔資產的 1/3，但實際上是 _____ / _____ ~ 41%，比想像中更痛&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="lessons-learned"&gt;Lessons Learned&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;沒有大到不能倒這件事&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;幣圈一天，人間十年，需要快速評估消息背後風險，不能下班再說&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="corrective-actions"&gt;Corrective Actions&lt;/h3&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;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>M1 MacBook DisplayLink 多螢幕心得</title><link>https://dwye.dev/post/macbook-extra-monitor/</link><pubDate>Mon, 01 Aug 2022 17:42:23 +0800</pubDate><guid>https://dwye.dev/post/macbook-extra-monitor/</guid><description>
&lt;h2 id="契機"&gt;契機&lt;/h2&gt;
&lt;p&gt;因為工作的關係拿到了一台 M1 MacBook，然而換成 M1 MacBook 之後就沒辦法直接使用 HDMI 接兩個外接螢幕了，這是 M1 晶片天生硬體的限制（M2 也是）。&lt;/p&gt;
&lt;p&gt;我原本習慣使用三螢幕工作，MacBook 自己的小螢幕加上兩個外接螢幕。試著用單一外接螢幕工作了一陣子，果然還是不習慣，所以就著手調查如何讓 M1 / M2 MacBook 也能外接兩個以上的螢幕。&lt;/p&gt;
&lt;p&gt;最後靠著 Amazon 海外網購的力量，找到了價格約 1500 台幣的解決方案：&lt;/p&gt;
&lt;h2 id="displaylink"&gt;DisplayLink&lt;/h2&gt;
&lt;p&gt;根據 &lt;a href="https://en.wikipedia.org/wiki/DisplayLink"&gt;wiki&lt;/a&gt;，DisplayLink 目前是新思科技（Synaptics）底下的一個技術，可以用 USB、Wifi 等方式來連接顯示器，實現一台電腦多螢幕的功能。要使用 DisplayLink，必須要有相對應的軟體以及硬體：&lt;/p&gt;
&lt;h3 id="軟體-displaylink-manager"&gt;軟體 DisplayLink Manager&lt;/h3&gt;
&lt;p&gt;這個好解決，直接在官網下載安裝即可：&lt;br&gt;
&lt;a href="https://www.synaptics.com/products/displaylink-graphics/downloads"&gt;https://www.synaptics.com/products/displaylink-graphics/downloads&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/displaylink/displaylink%20manager.jpeg" alt="DisplayLink Manager"&gt;&lt;/p&gt;
&lt;h3 id="硬體支援-displaylink-的-usb-轉接器"&gt;硬體：支援 DisplayLink 的 USB 轉接器&lt;/h3&gt;
&lt;p&gt;如果直接搜尋在台灣有賣的 DisplayLink Hub，選項很少而且很貴，例如：&lt;br&gt;
&lt;img src="https://dwye.dev/img/displaylink/owc.jpeg" alt="expensive DisplayLink Hub on PCHome"&gt;&lt;/p&gt;
&lt;p&gt;當然也可以上蝦皮或淘寶找&amp;hellip;但我就是不放心。&lt;/p&gt;
&lt;p&gt;所以就逛了下 DisplayLink 官網，發現官網有列出支援的設備還蠻多的：&lt;br&gt;
&lt;a href="https://www.synaptics.com/products/displaylink-graphics/displaylink-products"&gt;https://www.synaptics.com/products/displaylink-graphics/displaylink-products&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="最終方案-wavlink-usb-30-to-hdmi-adapter"&gt;最終方案: WAVLINK USB 3.0 to HDMI Adapter&lt;/h2&gt;
&lt;p&gt;我從上面的連結中找&lt;a href="https://www.synaptics.com/products/displaylink-graphics/displaylink-products-list?field_displaylink_category_value=usb_adapters"&gt;支援 DisplayLink 的 USB 連接器&lt;/a&gt;（因為我的 Hub 有多的 USB 孔可以使用，而 USB type-c 連接器的通常比較貴）&lt;/p&gt;
&lt;p&gt;最後我挑選了這隻 WAVLINK USB 3.0 to HDMI Adapter:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/displaylink/amazon.jpeg" alt="WAVLINK on Amazon"&gt;&lt;/p&gt;
&lt;p&gt;然後直接上 Amazon 買，從海外運進來，這樣含運費共 53 USD，大概 1500 台幣，還算可以接受的價格（實際上就是自己買水貨的概念）。&lt;br&gt;
當然，這邊是刷我的 &lt;a href="https://dwye.dev/post/crypto-com-visa-spotify-netflix/"&gt;Crypto.com 卡&lt;/a&gt;，省去海外刷卡手續費，還能賺 3 % CRO 回饋：&lt;br&gt;
&lt;img src="https://dwye.dev/img/displaylink/cro%20rebate.jpeg" alt="CRO Amazon Rebate"&gt;&lt;/p&gt;
&lt;p&gt;收到實品：&lt;br&gt;
&lt;img src="https://dwye.dev/img/displaylink/wavlink.jpeg" alt="WavLink Hub with Box"&gt;&lt;/p&gt;
&lt;p&gt;拆封：&lt;br&gt;
&lt;img src="https://dwye.dev/img/displaylink/wavlink%202.jpeg" alt="WavLink Hub"&gt;&lt;/p&gt;
&lt;p&gt;最後就可以插上 Hub 使用了～（後面那個 Hub 左邊就是這個新買的 WAVLINK adaptor，右邊則是直接用 HDMI 接我的主螢幕）&lt;br&gt;
&lt;img src="https://dwye.dev/img/displaylink/hubs.jpeg" alt="Hub"&gt;&lt;/p&gt;
&lt;h2 id="使用-displaylink-的一些缺點"&gt;使用 DisplayLink 的一些缺點&lt;/h2&gt;
&lt;p&gt;當然，使用 USB 來當作顯示器入口還是有缺點的，因為多經過了一層軟體，而不是原生支援，目前查到和遇到的缺點：&lt;/p&gt;
&lt;h3 id="netflix-黑屏--黑畫面"&gt;Netflix 黑屏 / 黑畫面&lt;/h3&gt;
&lt;p&gt;因為 DRM 和 DisplayLink 的技術衝突，沒辦法在開著 DisplayLink 的情況下觀看 Netflix（沒有實測，不過其他有做 DRM 保護的線上串流影音應該也一樣，例如 Display+, KKTV&amp;hellip;）。如果要看 Netflix 勢必就要只使用單一外接螢幕。&lt;/p&gt;
&lt;p&gt;我自己的實測，不需要把 DisplayLink Manager 軟體關掉，只需要把 DisplayLink Manager 裡面顯示器名稱旁的開關關掉就好，這時候第二個外接螢幕就會斷掉連接。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/displaylink/off.jpeg" alt="Turn Off DisplayLink"&gt;&lt;br&gt;
這時候就可以正常觀看 Netflix 了。&lt;/p&gt;
&lt;p&gt;看完 Netflix 想要用多個螢幕時，只要把 DisplayLink Manager 顯示器名稱旁的開關重新打開，第二個外接螢幕就會重新連上線。&lt;/p&gt;
&lt;p&gt;雖然不方便，但還是可以忍受，畢竟不會在看 Netflix 的時候多螢幕工作嘛（大家應該都是吧 XD）。&lt;/p&gt;
&lt;h3 id="更新率"&gt;更新率&lt;/h3&gt;
&lt;p&gt;就我所知，DisplayLink 所支援的最大更新率是 60 Hz。還好我的副螢幕最大更新率也是 60 Hz，因此躲開了這個缺點。&lt;/p&gt;
&lt;h3 id="延遲"&gt;延遲&lt;/h3&gt;
&lt;p&gt;畢竟 DisplayLink 多了一層軟體轉換，並非原生外接，因此勢必會有稍微的延遲。不過因為我沒有用 DisplayLink 打遊戲的需求，日常使用上的延遲是無感的。（相較起來，MacBook 中文輸入的延遲都有感的多&amp;hellip;.）&lt;/p&gt;
&lt;h2 id="其他解決方案"&gt;其他解決方案&lt;/h2&gt;
&lt;p&gt;當然，採取 DisplayLink 方案之前，我也有調查其他可能花更多錢或更省錢的方案，包含：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;直接買一個超大曲面螢幕（20:9 或 32:9）取代兩個外接螢幕，這樣就只有單一外接螢幕了，然後讓兩個舊螢幕退休（超貴）。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ptt.cc/bbs/MAC/M.1654962256.A.7F1.html"&gt;這篇&lt;/a&gt; PTT 文章提到的 Lemorele 的連接器，說實在感覺只是另一種和 DisplayLink 打對台的技術。&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>秋口幹你娘</title><link>https://dwye.dev/post/choco-fxxk-you-mom/</link><pubDate>Mon, 18 Jul 2022 15:01:56 +0800</pubDate><guid>https://dwye.dev/post/choco-fxxk-you-mom/</guid><description>
&lt;p&gt;常常在股癌的 podcast 節目中聽到有觀眾留言秋口幹你娘的梗。就好奇是從哪集來的，查了一陣子才找到是第幾集：&lt;br&gt;
答案就是 EP. 117 這集的 12:47 處。&lt;/p&gt;
&lt;p&gt;&lt;a href="https://youtu.be/FiAkwtN0NZc?t=767"&gt;Youtube 連結&lt;/a&gt;在這：&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/FiAkwtN0NZc?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;對，股癌隨口一罵就成了經典。&lt;br&gt;
希望可以幫到跟我一樣好奇的網友/聽眾。&lt;/p&gt;
&lt;p&gt;（自己架網站發廢文的我。）&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>Crypto.com Visa 卡 ATM 提款與匯率實測</title><link>https://dwye.dev/post/crypto-com-atm-withdraw/</link><pubDate>Tue, 17 May 2022 02:11:25 +0800</pubDate><guid>https://dwye.dev/post/crypto-com-atm-withdraw/</guid><description>
&lt;p&gt;在前一篇&lt;a href="https://dwye.dev/post/crypto-com-visa-spotify-netflix/"&gt;辦卡與申請回饋的心得文&lt;/a&gt;中，有提到我的這張 Crypto.com Visa 卡支援一定額度內的免手續費 ATM 提款。這篇就來分享我的實測提款結果。&lt;br&gt;
&lt;img src="https://dwye.dev/img/crypto-withdraw/cro_visa.jpg" alt="cro green"&gt;&lt;/p&gt;
&lt;p&gt;我的卡等是 Jade Green，所以每個月有 800 SGD 額度的免手續費 ATM 提款，以現在的匯率 21.38 塊來計算，可以領 17104 元台幣。當然，為了避免時間造成的匯率差異，所以還是以 17000 來記會比較好。&lt;/p&gt;
&lt;h2 id="atm-選擇"&gt;ATM 選擇&lt;/h2&gt;
&lt;p&gt;需要找支援 Visa 提款的 ATM，同時不同家銀行也可能再額外收手續費，因此需要注意。下面整理了熱心網友們實測不需要手續費的 ATM：&lt;/p&gt;
&lt;h3 id="免手續費-atm"&gt;免手續費 ATM&lt;/h3&gt;
&lt;p&gt;根據&lt;a href="http://jacobmei.blogspot.com/2021/11/cryptocom-visa-card.html"&gt;這篇：新手筆記：Crypto.com Visa Card 註冊與申請使用的心得整理&lt;/a&gt;，有提供一個 ATM 對於整理：（偷偷說其實 google 後發現都是同一份名單在流傳 XD，只有少許更新）&lt;/p&gt;
&lt;p&gt;需要費用 ($100/筆)：中信、台新、永豐。&lt;/p&gt;
&lt;p&gt;不須費用：臺銀、合庫、土銀、第一、華南、彰銀、上海、富邦、國泰、高雄、兆豐、花旗、渣打、台中、匯豐、中小企銀、新光、板信、聯邦、遠東、元大、玉山、日盛、安泰。&lt;/p&gt;
&lt;p&gt;無法使用：農業金庫、京城、瑞興、華泰、陽信、三信、凱基、台北五信、士林區農會、郵局。&lt;/p&gt;
&lt;p&gt;簡單來說，只需要避開需要費用的幾家，就可以了。&lt;/p&gt;
&lt;h2 id="atm-操作實測"&gt;ATM 操作實測&lt;/h2&gt;
&lt;p&gt;不巧的是剛好我常用的就是三家會收手續費的：中信（薪轉戶）、台新（Richart）和永豐（大戶）&amp;hellip;。&lt;/p&gt;
&lt;p&gt;最後我選擇則用臺灣銀行做實測。&lt;/p&gt;
&lt;p&gt;總之把卡插進去，選擇信用卡的部分：&lt;br&gt;
&lt;img src="https://dwye.dev/img/crypto-withdraw/atm1.jpg" alt="atm operation 1"&gt;&lt;/p&gt;
&lt;p&gt;提款！（&lt;del&gt;平常都當韭菜被大戶提款，終於換我來提款了&lt;/del&gt;）&lt;br&gt;
&lt;img src="https://dwye.dev/img/crypto-withdraw/atm2.jpg" alt="atm operation 2"&gt;&lt;/p&gt;
&lt;p&gt;一樣選信用卡帳戶：&lt;br&gt;
&lt;img src="https://dwye.dev/img/crypto-withdraw/atm3.jpg" alt="atm operation 3"&gt;&lt;/p&gt;
&lt;p&gt;中間可能會要你輸入密碼，就是 Crypto.com App 的提領密碼。&lt;/p&gt;
&lt;p&gt;這次測試我提領了 3000 塊，可以看到收據，ATM 的確是沒有額外收手續費的：&lt;br&gt;
&lt;img src="https://dwye.dev/img/crypto-withdraw/receipt.jpg" alt="atm operation receipt"&gt;&lt;/p&gt;
&lt;p&gt;App 內可以看到提款記錄：&lt;br&gt;
&lt;img src="https://dwye.dev/img/crypto-withdraw/record.jpg" alt="atm operation record in app"&gt;&lt;/p&gt;
&lt;h2 id="匯率與匯差計算"&gt;匯率與匯差計算&lt;/h2&gt;
&lt;p&gt;這次提款的時候，當下的告示買入即期匯率是 21.29，提款後帳戶內減少了 141.7 SGD，所以實際上的匯率是 3000 / 141.7 = 21.17，和當下的匯率相當接近了，可以說匯率是很划算的。&lt;/p&gt;
&lt;p&gt;可以使用兩個匯率計算匯差： (21.29 - 21.17) / 21.29 = 0.56 %&lt;/p&gt;
&lt;p&gt;還是有約 0.5 % 的匯差，所以並不是完全無痛提款的喔！但比起很多出金方式相比，這張卡已經提供相當優惠的小額出金方式了，而且和匯率浮動相比，也不是會讓人特別在意的數字就是。&lt;/p&gt;
&lt;h2 id="參考資料"&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://jacobmei.blogspot.com/2021/11/cryptocom-visa-card.html"&gt;新手筆記：Crypto.com Visa Card 註冊與申請使用的心得整理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@oofoofoof/mco-visa-card-%E4%BB%8B%E7%B4%B9%E5%8F%8A%E5%AF%A6%E6%B8%AC-e790d3d72dd8"&gt;Crypto.com Visa Card 深度分析實測及避險&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://matters.news/@erica072/272066"&gt;加密貨幣簽帳卡〔Crypto.com &amp;amp; Wirex〕使用心得更新版！！！&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>放棄 Disqus 開始使用 utterances 作為 GitHub Page 的留言板</title><link>https://dwye.dev/post/no-disqus-but-utterances/</link><pubDate>Sat, 23 Apr 2022 17:49:26 +0800</pubDate><guid>https://dwye.dev/post/no-disqus-but-utterances/</guid><description>
&lt;p&gt;最近發現 Disqus 留言系統會擅自投放一堆廣告，上網查了一下，才發現&lt;a href="https://blog.gslin.org/archives/2021/02/11/9960/%E5%8E%9F%E4%BE%86-disqus-%E5%B7%B2%E7%B6%93%E8%A2%AB%E7%B6%B2%E8%B7%AF%E5%BB%A3%E5%91%8A%E5%85%AC%E5%8F%B8%E8%B2%B7%E8%B5%B0%E4%BA%86/"&gt;Disqus 早就被網路廣告公司買走了&lt;/a&gt;，不只會追蹤使用者行為，更開始塞一堆醜醜的廣告（最不能接受的點&amp;hellip;我自己都因為美觀不敢亂放廣告了），一氣之下決定換掉他。&lt;/p&gt;
&lt;p&gt;剛好最近注意&lt;a href="https://blog.huli.tw/"&gt;胡立大大的網站&lt;/a&gt;使用的留言系統 utterances 十分美觀，查了一下是利用 GitHub issue 來實作留言系統，直接把 GitHub issue 當作儲存資料的地方，不用自架 server，設置也快速，又是 open source 的，雖然沒有訪客留言功能差強人意，但考量到我最近大部分文章都是資訊圈相關的東西，也為了美觀 XD 就還是採用了。&lt;/p&gt;
&lt;h2 id="utterances-的設置"&gt;utterances 的設置&lt;/h2&gt;
&lt;p&gt;對於 GitHub Page 的用戶，設置 utterances 相當簡單，只需要做下面幾件事：&lt;br&gt;
（也可以參考官網：https://utteranc.es/）&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;選擇一個公開的 GitHub Repo 作為 comments 的資料庫（會使用 issue 的功能，不會影響 Code，所以可以選擇和 Host GitHub Page 相同的 Repo）。&lt;/li&gt;
&lt;li&gt;在那個 Repo 安裝 &lt;a href="https://github.com/apps/utterances"&gt;utterances app&lt;/a&gt;，這是一個 GitHub App，上架於 GitHub 官方的 Marketplace，因此是可信任的。&lt;/li&gt;
&lt;li&gt;在 HTML 中插入 utterances 的 Script。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="我的更新方式"&gt;我的更新方式&lt;/h3&gt;
&lt;p&gt;首先安裝在 Github Repo 安裝 utterances app，記得選擇只安裝在目標 Repo。這邊直接使用這個網站背後的 Repo &lt;code&gt;dwy6626/dwy6626.github.io&lt;/code&gt; 作為 issue 存放地：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/install-utterances.jpg" alt="setup utterance"&gt;&lt;/p&gt;
&lt;p&gt;接著修改我的 Hugo 主題: &lt;a href="https://github.com/dwy6626/dw-favored-blackburn/commit/c611e1175ed631f08bc0df4fcc55047ad2b8c4cd"&gt;dw-favored-blackburn&lt;/a&gt; 背後的 HTML Template:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{{ with .Site.Params.utteranc }}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;script&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;src&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;https://utteranc.es/client.js&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;repo&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;{{ .repo }}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;issue-term&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;pathname&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;theme&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;github-light&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;crossorigin&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;anonymous&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;async&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/&lt;span style="color:#f92672"&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以參考這個 &lt;a href="https://github.com/dwy6626/dw-favored-blackburn/commit/c611e1175ed631f08bc0df4fcc55047ad2b8c4cd"&gt;commit&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;之後就可以在 &lt;code&gt;config.toml&lt;/code&gt; 直接設定：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[&lt;span style="color:#a6e22e"&gt;params&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [&lt;span style="color:#a6e22e"&gt;params&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;utteranc&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;repo&lt;/span&gt; = &lt;span style="color:#e6db74"&gt;&amp;#34;dwy6626/dwy6626.github.io&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這樣就完成設置了。&lt;/p&gt;
&lt;p&gt;如果你也是使用我的 Hugo Template，只需要安裝 app 後，在 &lt;code&gt;config.toml&lt;/code&gt; 加入 repo 設定即可～&lt;/p&gt;
&lt;p&gt;接著就可以跑 &lt;code&gt;hugo serve&lt;/code&gt;，看看有沒有設置成功。如果成功的話，在 localhost 就可以看到：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/utterances-demo.jpg" alt="utterance demo"&gt;&lt;/p&gt;
&lt;p&gt;（上面是截圖，不要在這邊留言喔 XD）&lt;/p&gt;
&lt;p&gt;然後就大功告成了～&lt;/p&gt;
&lt;h2 id="留言測試"&gt;留言測試&lt;/h2&gt;
&lt;p&gt;當在文章內留言送出後，utterances 就會幫你在 GitHub Repo 的自動創建該文章對應的 issue:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/github-issue-utterance.jpg" alt="utterance demo on github issue"&gt;&lt;/p&gt;
&lt;p&gt;點進去就可以看到留言其實存放在這了：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/github-issue-utterance-post.jpg" alt="utterance demo on github issue 2"&gt;&lt;/p&gt;
&lt;h2 id="其他可行解法"&gt;其他可行解法&lt;/h2&gt;
&lt;p&gt;因為我的 blog 還有以前家教相關的文章，客群就比較不是面向電資圈的人，其實不支援訪客留言還蠻可惜的，所以我也有參考一下其他的可能方案，主要留言板通常有三種類型：&lt;/p&gt;
&lt;h3 id="第三方服務"&gt;第三方服務&lt;/h3&gt;
&lt;p&gt;如 Disqus，或是 &lt;a href="https://www.talkyard.io/blog-comments"&gt;Talkyard&lt;/a&gt;、&lt;a href="https://commento.io/"&gt;commento&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;優點是方便設置，功能通常較完善，缺點則是免費版通常有所限制，不是要付費，不然就是會被插廣告，就算現在沒有廣告，以後可能也會有（就像 Disqus 一樣）。&lt;/p&gt;
&lt;p&gt;因為我不想以後再做類似的事情，因此不考慮第三方服務。&lt;/p&gt;
&lt;h3 id="自架留言板"&gt;自架留言板&lt;/h3&gt;
&lt;p&gt;如 &lt;a href="https://posativ.org/isso/"&gt;isso&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;優點也是功能完善，而且通常提供簡單的架設方式，只需要有個簡單的機器就能架設留言板後端，缺點也是你要有個簡單的機器。&lt;/p&gt;
&lt;p&gt;其實現在雲端服務多半有每個月一定的免費額度，流量不大的 blog，可能開一台小機器可以壓在免費額度內。&lt;/p&gt;
&lt;p&gt;但是維護起來總是麻煩，也怕不小心燒到需要交錢，因此還是沒採用。&lt;/p&gt;
&lt;h3 id="github-issue"&gt;GitHub Issue&lt;/h3&gt;
&lt;p&gt;這篇介紹的 utterances 其實就是。&lt;/p&gt;
&lt;p&gt;把資料存在 GitHub Issue 然後用 GitHub API，真的是各種善用 GitHub 功能，我超喜歡的這種概念。&lt;/p&gt;
&lt;p&gt;對於流量不大的小部落格，應該不用怕達到 API limit 被 GitHub 官方關切。加上本身也沒什麼依賴，開源的情況就算以後套件想偷插廣告，只要鎖住版本不更新就好了。（當然，除非 GitHub 官方自己限制這種用法就是，但小量使用應該不至於被查水表哈哈。）&lt;/p&gt;
&lt;p&gt;缺點就是，必須要訪客有 GitHub 帳號的情況才能留言，功能比較差強人意就是。但對於一個工程師的 Blog，或許也是足夠了。而且比較嚴格的留言機制，也可以擋住很多垃圾留言～&lt;/p&gt;
&lt;p&gt;所以最後再三權衡下還是使用這個解方。&lt;/p&gt;
&lt;h2 id="參考資料"&gt;參考資料&lt;/h2&gt;
&lt;h3 id="docs"&gt;Docs&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://utteranc.es/"&gt;utterances 官方頁面&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gohugo.io/content-management/comments/"&gt;Hugo Comments 官方文件&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="blog-posts"&gt;Blog Posts&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.gslin.org/archives/2021/02/11/9960/%E5%8E%9F%E4%BE%86-disqus-%E5%B7%B2%E7%B6%93%E8%A2%AB%E7%B6%B2%E8%B7%AF%E5%BB%A3%E5%91%8A%E5%85%AC%E5%8F%B8%E8%B2%B7%E8%B5%B0%E4%BA%86/"&gt;原來 Disqus 已經被網路廣告公司買走了&amp;hellip;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.jkg.tw/p3350/"&gt;Utterances 免費開源、無廣告、無追蹤的網站留言系統&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>Crypto.com Visa 卡 Netflix 和 Spotify 台灣設定心得（2022-05 更新）</title><link>https://dwye.dev/post/crypto-com-visa-spotify-netflix/</link><pubDate>Sat, 09 Apr 2022 02:11:25 +0800</pubDate><guid>https://dwye.dev/post/crypto-com-visa-spotify-netflix/</guid><description>
&lt;p&gt;在幣圈打滾一段時間，終於入手了幣圈最潮的一張卡，Crypto.com Visa 卡：&lt;br&gt;
&lt;img src="https://dwye.dev/img/crypto/cro_visa.jpg" alt="cro green and anny"&gt;&lt;br&gt;
（卡片與我們家的狗狗）&lt;/p&gt;
&lt;p&gt;我的卡等是 Jade Green，有 3% CRO 幣回饋，和 &lt;strong&gt;100% 的 Netflix, Spotify 回饋&lt;/strong&gt;。這篇要來講關於這張卡的設定遇到的疑難雜症和一些個人心得。&lt;br&gt;
&lt;em&gt;（註：&lt;a href="https://help.crypto.com/zh-TW/articles/2742447-crypto-com-visa-%E5%8D%A1-%E7%8D%8E%E5%8B%B5%E5%92%8C%E7%A6%8F%E5%88%A9"&gt;2022-05 有權益修正，於 6 月生效&lt;/a&gt;）&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;註：隨著時間福利可能有所變動，可以到官網看最新消息：&lt;a href="https://crypto.com/cards"&gt;https://crypto.com/cards&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="辦卡理由"&gt;辦卡理由&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;潮。&lt;/li&gt;
&lt;li&gt;資產分配：滿手現金，趁不是牛市時慢慢加碼幣圈，順便拿張卡可以緊急時提款（800 SGD/月的免手續費 ATM 提款，之後 2%）。可以參考我的&lt;a href="https://dwye.dev/post/crypto-com-atm-withdraw/"&gt;這篇實測&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;3% 無上限 CRO 回饋（而且沒有海外消費手續費，等同海外 4.5 % 優惠）&lt;em&gt;-&amp;gt; 將修正成 2 % 以及 50 USD 回饋上限，可以刷 2500 USD 約 74000 台幣&lt;/em&gt;。&lt;/li&gt;
&lt;li&gt;日常消費 = 變相出金。&lt;/li&gt;
&lt;li&gt;讓女友聽歌不用聽廣告（我是 Apple Music 用戶了）。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;列了這麼多，我覺得潮還是最重要的理由。&lt;/p&gt;
&lt;h2 id="使用心得"&gt;使用心得&lt;/h2&gt;
&lt;p&gt;消費通路其實蠻多&lt;a href="https://creditcards.com.tw/debit-card/cro-debit-card-spotify-netflix/"&gt;整理&lt;/a&gt;的，這邊就不贅述。一般來說只要不是小額付款，大部分都刷得過，行動支付能搭配 Line Pay 使用，Line Pay 的通路也十分廣，除了超商之外的 Line Pay 應該都能橫著走。&lt;/p&gt;
&lt;h3 id="spotify-回饋"&gt;Spotify 回饋&lt;/h3&gt;
&lt;p&gt;這個簡單，結帳的時候&lt;strong&gt;國家選新加坡&lt;/strong&gt;，就可以直接刷過去了。我刷的是家庭方案，刷完馬上就能拿到回饋：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/crypto/spotify-rebate.jpg" alt="cro spotify rebate"&gt;&lt;/p&gt;
&lt;h3 id="netflix-回饋"&gt;Netflix 回饋&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;懶人包：刷自己的卡，跟客服要回饋。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;這個比較麻煩，因為今年開始 Netflix 都是從新加坡請款，新加坡政府 MAS 的規定下，非新加坡用戶無法刷新加坡消費，因此台灣不能直接刷 Crypto.com 信用卡。所以大家都是先刷自己的卡，再把截圖搜集給客服跟他們要錢。&lt;/p&gt;
&lt;p&gt;詳細可以參考這系列方格子文章圖文教學，說得蠻清楚的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://vocus.cc/article/61ebbcd8fd897800014428d9"&gt;CRO卡片回饋申請方式&lt;br&gt;
&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vocus.cc/article/62072ef8fd897800014efe4c"&gt;Netflix回饋/CRO visa card 客服後續&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;和客服聯絡的部分我出了一點小意外，因為客服沒有意識到台灣不能直接刷 Netflix 這件事。但也不用怕，網路上一堆成功案例，就好好說明情況就好。他一開始跟我要銀行證明，但我直接回他還沒入帳，並把準備好的信用卡自拍照與發票傳給他問他這樣行不行得通。可見 Crypto.com 的客服也是蠻能溝通的，別因為英文就退縮了～。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/crypto/chat-netflix-rebate.jpg" alt="cro netflix rebate"&gt;&lt;/p&gt;
&lt;p&gt;看來這個回饋下個月就能入帳了，是來得慢了點，但沒關係。&lt;/p&gt;
&lt;h4 id="2022-05-更新"&gt;2022-05 更新&lt;/h4&gt;
&lt;p&gt;雖然不是月初，但還是拿到回饋了，而且連同四月份的也補上來了，感謝 Crypto.com 爸爸讓我看 Netflix（雖然我質押的 CRO 跌慘了，哭）。&lt;br&gt;
&lt;img src="https://dwye.dev/img/crypto/netflix-rebate.jpg" alt="cro netflix rebate"&gt;&lt;/p&gt;
&lt;h2 id="常見問題"&gt;常見問題&lt;/h2&gt;
&lt;h3 id="如何入金"&gt;如何入金&lt;/h3&gt;
&lt;p&gt;因為 Crypto.com Visa 卡是儲值卡，必須要戶頭有錢才能使用。推薦的入金方式有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;信用卡入金&lt;/strong&gt;：Crypto.com 申辦的前一個月，免收信用卡入金手續費，趁這個時候拿有海外交易回饋的卡多刷一點（但要注意有沒有被銀行排除回饋項目喔）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;加密貨幣入金&lt;/strong&gt;：一般來說我還是習慣走 MAX 台幣入金買 USDT -&amp;gt; FTX Pro 換目標貨幣 -&amp;gt; Crypto.com App。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;電匯入金&lt;/strong&gt;：金額大的話也可以用外幣帳戶直接電匯入金 USD，相對手續費會較低。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;交易所有錢之後，還要額外儲值法幣到 Crypto.com 付款卡裡面，算是比較麻煩的部分：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/crypto/crypto-com-debit.jpg" alt="crypto.com debit"&gt;&lt;/p&gt;
&lt;p&gt;儲值的幣種是有限制的，可以參考&lt;a href="https://help.crypto.com/zh-TW/articles/2833449-%E5%A6%82%E4%BD%95%E5%84%B2%E5%80%BC-crypto-com-visa-%E5%8D%A1-%E4%BA%9E%E5%A4%AA%E5%8D%80"&gt;官方文件&lt;/a&gt;。一般是推薦 USDC。比較可惜的是 CRO 幣沒辦法直接儲值，還需要換成 USDC 或其他加密貨幣才能儲值。交易部分可以參考下一段：&lt;/p&gt;
&lt;h3 id="cryptocom-app-的交易功能推薦嗎"&gt;Crypto.com App 的交易功能推薦嗎？&lt;/h3&gt;
&lt;p&gt;以方便性論，推薦。&lt;br&gt;
以價格論，不推薦。&lt;/p&gt;
&lt;p&gt;他的運作模式可以想像成 &lt;a href="https://www.blocktempo.com/taiwan-quantitative-trading-use-dark-pool-to-fulfill-0-trading-fee/"&gt;Robinhood 的暗池模式&lt;/a&gt;，並不是掛單，而是交易所會賺取買賣的報價差異。&lt;/p&gt;
&lt;h3 id="cryptocom-有網頁版嗎"&gt;Crypto.com 有網頁版嗎？&lt;/h3&gt;
&lt;p&gt;有 &lt;a href="https://crypto.com/exchange"&gt;Crypto.com Exchange&lt;/a&gt;，但和 app 是分開的帳密，分開的資產管理。&lt;br&gt;
只有 app 可以申請和管理付款卡。&lt;/p&gt;
&lt;h2 id="cryptocom-app-推薦碼"&gt;Crypto.com App 推薦碼&lt;/h2&gt;
&lt;p&gt;如果你覺得這邊文章對你有幫助，可以透過我的推薦連結 &lt;a href="https://crypto.com/app/kz7cvp7r97"&gt;https://crypto.com/app/kz7cvp7r97&lt;/a&gt; 在 Crypto.com App 註冊，一起獲得 25 USD 等值的 CRO 幣！&lt;/p&gt;
&lt;p&gt;或是在 App 內註冊時使用我的推薦碼 kz7cvp7r97 也可以！&lt;/p&gt;
&lt;h2 id="參考資料"&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medium.com/@oofoofoof/mco-visa-card-%E4%BB%8B%E7%B4%B9%E5%8F%8A%E5%AF%A6%E6%B8%AC-e790d3d72dd8"&gt;Crypto.com Visa Card 深度分析實測及避險&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ptt.cc/bbs/DigiCurrency/M.1637426917.A.B16.html"&gt;PTT：[交易] Crypto.com 註冊、卡片升級等常見問題&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://home.gamer.com.tw/artwork.php?sn=5368404"&gt;巴哈：【文章】Crypto.com Visa卡 簡易使用及綁定心得 #2022.02.03 支援中油&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vocus.cc/article/61ebbcd8fd897800014428d9"&gt;方格子：CRO卡片回饋申請方式&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://creditcards.com.tw/debit-card/cro-debit-card-spotify-netflix/"&gt;2022 CRO金融卡優惠介紹，Spotify/Netflix最高100%回饋｜金融卡&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以及腦哥的影片們：&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/pMiXyWvACDU?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/3meVnadU2og?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>Python 實作 LRU Cache (LeetCode 146)</title><link>https://dwye.dev/post/python-lru/</link><pubDate>Wed, 16 Feb 2022 18:41:25 +0800</pubDate><guid>https://dwye.dev/post/python-lru/</guid><description>
&lt;h2 id="題目"&gt;題目&lt;/h2&gt;
&lt;p&gt;LeetCode 146 的名稱就叫 &lt;a href="https://leetcode.com/problems/lru-cache/"&gt;LRU Cache&lt;/a&gt;，算是教科書等級的題目，希望你可以實作一個 cache，在內部的 Cache Key 有著透過 LRU 演算法的淘汰機制（最久沒影使用的 Key 會優先淘汰）。&lt;/p&gt;
&lt;p&gt;時間複雜度限制 get 和 set 都要 O(1)。&lt;/p&gt;
&lt;h2 id="思路"&gt;思路&lt;/h2&gt;
&lt;p&gt;首先複習一下 LRU 的 get 和 set 要做的事情。為了實現 LRU，每個 key 必須要跟著一個 priority（優先度）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;get 時要把該 key 的 priority 拉到最高，讓這個 key 在刪除時永遠是最後一個被考慮的。&lt;/li&gt;
&lt;li&gt;put 時，要維護並檢查目前的 capacity（容量），若 capacity 不足，則需要把 priority 最低的 cache 刪除。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;這兩件事情都要在 O(1) 內達成。&lt;/p&gt;
&lt;h3 id="hash-map-dictionary"&gt;Hash Map (Dictionary)&lt;/h3&gt;
&lt;p&gt;Dictionary 是 Python 實作的 Hash map，並且天生防止了 Hash collision，又擁有 Hash Map O(1) 存取的特性，因此很適合拿來做 cache。&lt;/p&gt;
&lt;p&gt;然而光是用一個 Hash Map 沒辦法 O(1) 找出 priority 最小的 key 並刪除。&lt;/p&gt;
&lt;h3 id="linked-list"&gt;Linked List&lt;/h3&gt;
&lt;p&gt;Linked List 中的 Doubly Linked List 具有 O(1) insert / delete 的特性，可以透過讓 Node 的位置來表示優先度，每次 get 時，把該 Node 放到「第一個」，而每次 put 時，若 capacity 不足則移除「最後一個」Node 所代表的 Key，達到在 O(1) 時間內維護 priority。&lt;/p&gt;
&lt;p&gt;但如果要查找某個 key 代表的 Node，單用 Linked List 只能達到 O(n) 的時間複雜度，因為要遍歷 List 去尋找 Node。&lt;/p&gt;
&lt;h3 id="linked-list-搭配-hash-map"&gt;Linked List 搭配 Hash Map&lt;/h3&gt;
&lt;p&gt;上面提到的兩個資料結構相輔相成。兩個都使用，就能達到 O(1) 存取和 O(1) 維護 priority。&lt;/p&gt;
&lt;p&gt;可以參考這個示意圖，get 的時候透過 hash map 找到 Node，並把該 Node 搬到 head 來維護 priority:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/lru_get.jpg" alt="lru get"&gt;&lt;/p&gt;
&lt;h2 id="實作"&gt;實作&lt;/h2&gt;
&lt;p&gt;先來定義 Linked List 裡面放的 Node：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-Python" data-lang="Python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Node&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__init__&lt;/span&gt;(self, key: int&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;, val: int&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;key &lt;span style="color:#f92672"&gt;=&lt;/span&gt; key
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;val &lt;span style="color:#f92672"&gt;=&lt;/span&gt; val
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;next &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;prev &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;接著是 LRU Cache 的部分。&lt;br&gt;
在頭尾都使用 dummy node，來減少繁瑣的邊界條件處理，也算是 Linked List 常用的技巧了。&lt;br&gt;
有了 dummy node，原本我們的「第一個」Node，就是 head 的下一個。而「最後一個」Node，就是 tail 的上一個。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-Python" data-lang="Python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;LRUCache&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__init__&lt;/span&gt;(self, capacity: int):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;head &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Node()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;tail &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Node()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;capacity &lt;span style="color:#f92672"&gt;=&lt;/span&gt; capacity
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 別忘了把頭尾接起來&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;head&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;tail)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這裡偷偷先使用了 &lt;code&gt;append&lt;/code&gt; 這個方法。他應該要是個可以幫我們把某個 Node 和另一個 Node 連在一起的 method，回到 Node Class 補上：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-Python" data-lang="Python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Node&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;append&lt;/span&gt;(self, other: &lt;span style="color:#e6db74"&gt;&amp;#39;Node&amp;#39;&lt;/span&gt;) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;next &lt;span style="color:#f92672"&gt;=&lt;/span&gt; other
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; other&lt;span style="color:#f92672"&gt;.&lt;/span&gt;prev &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;接著來處理 LRU cache 的核心 method。從 get 先開始：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-Python" data-lang="Python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;LRUCache&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(self, key: int) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; int:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# cache miss&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; key &lt;span style="color:#f92672"&gt;not&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# cache hit，調整 priority&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; node &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map[key] &lt;span style="color:#75715e"&gt;# Hash map 的 O(1) 查找&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; node&lt;span style="color:#f92672"&gt;.&lt;/span&gt;pull() &lt;span style="color:#75715e"&gt;# 又是一個新方法，可以想像成把這個 Node 拉出來 &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; node&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;head&lt;span style="color:#f92672"&gt;.&lt;/span&gt;next) &lt;span style="color:#75715e"&gt;# 把這個 Node 拉到「第一個」，也就是把原本的第一個接在後面&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;head&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(node) &lt;span style="color:#75715e"&gt;# 然後接到 dummy head 後方&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 回傳&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; node&lt;span style="color:#f92672"&gt;.&lt;/span&gt;val
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這邊又偷偷用了一個新方法：&lt;code&gt;pull&lt;/code&gt;。他應該要是個可以讓我們把這個 Node「取出」原本的位置，把他原先前後的 Node 接起來：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-Python" data-lang="Python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Node&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pull&lt;/span&gt;(self) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;next:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;next&lt;span style="color:#f92672"&gt;.&lt;/span&gt;prev &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;prev
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;prev:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;prev&lt;span style="color:#f92672"&gt;.&lt;/span&gt;next &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;next
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;到這邊已經完成一半了。&lt;br&gt;
接下來是加入 key 和刪除最少使用的 node 的部分：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-Python" data-lang="Python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;LRUCache&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;put&lt;/span&gt;(self, key: int, value: int) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 如果已經有了，就不需要插入新的 Node，只需要更新值就好&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; key &lt;span style="color:#f92672"&gt;in&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; node &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map[key]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; node&lt;span style="color:#f92672"&gt;.&lt;/span&gt;val &lt;span style="color:#f92672"&gt;=&lt;/span&gt; value
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; node&lt;span style="color:#f92672"&gt;.&lt;/span&gt;pull()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# initialize 新的 Node:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map[key] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Node(key, value)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;capacity &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;capacity &lt;span style="color:#f92672"&gt;-=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# capacity 不夠，要刪除 LRU&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; discard &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;tail&lt;span style="color:#f92672"&gt;.&lt;/span&gt;prev &lt;span style="color:#75715e"&gt;# 因為 capacity &amp;gt; 1，一定是非 dummy node&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; discard&lt;span style="color:#f92672"&gt;.&lt;/span&gt;pull() &lt;span style="color:#75715e"&gt;# 斷開連結！&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;del&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map[discard&lt;span style="color:#f92672"&gt;.&lt;/span&gt;key] &lt;span style="color:#75715e"&gt;# 在 Node Class 定義時記住的 key 派上用場了&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;del&lt;/span&gt; discard
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 當然，和 get 一樣，剛使用過/加入的 Node 也要放在「第一個」&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; node &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map[key]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; node&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;head&lt;span style="color:#f92672"&gt;.&lt;/span&gt;next)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;head&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(node)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="完整-code"&gt;完整 Code&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-Python" data-lang="Python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Node&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__init__&lt;/span&gt;(self, key: int&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;, val: int&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;key &lt;span style="color:#f92672"&gt;=&lt;/span&gt; key
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;val &lt;span style="color:#f92672"&gt;=&lt;/span&gt; val
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;next &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;prev &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;append&lt;/span&gt;(self, other: &lt;span style="color:#e6db74"&gt;&amp;#39;Node&amp;#39;&lt;/span&gt;) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;next &lt;span style="color:#f92672"&gt;=&lt;/span&gt; other
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; other&lt;span style="color:#f92672"&gt;.&lt;/span&gt;prev &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pull&lt;/span&gt;(self) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;next:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;next&lt;span style="color:#f92672"&gt;.&lt;/span&gt;prev &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;prev
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;prev:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;prev&lt;span style="color:#f92672"&gt;.&lt;/span&gt;next &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;next
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;LRUCache&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__init__&lt;/span&gt;(self, capacity: int):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;head &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Node()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;tail &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Node()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;capacity &lt;span style="color:#f92672"&gt;=&lt;/span&gt; capacity
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 別忘了把頭尾接起來&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;head&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;tail)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(self, key: int) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; int:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# cache miss&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; key &lt;span style="color:#f92672"&gt;not&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# cache hit，調整 priority&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; node &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map[key] &lt;span style="color:#75715e"&gt;# Hash map 的 O(1) 查找&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; node&lt;span style="color:#f92672"&gt;.&lt;/span&gt;pull() &lt;span style="color:#75715e"&gt;# 又是一個新方法，可以想像成把這個 Node 拉出來 &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; node&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;head&lt;span style="color:#f92672"&gt;.&lt;/span&gt;next) &lt;span style="color:#75715e"&gt;# 把這個 Node 拉到「第一個」，也就是把原本的第一個接在後面&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;head&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(node) &lt;span style="color:#75715e"&gt;# 然後接到 dummy head 後方&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 回傳&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; node&lt;span style="color:#f92672"&gt;.&lt;/span&gt;val
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;put&lt;/span&gt;(self, key: int, value: int) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 如果已經有了，就不需要插入新的 Node，只需要更新值就好&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; key &lt;span style="color:#f92672"&gt;in&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; node &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map[key]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; node&lt;span style="color:#f92672"&gt;.&lt;/span&gt;val &lt;span style="color:#f92672"&gt;=&lt;/span&gt; value
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; node&lt;span style="color:#f92672"&gt;.&lt;/span&gt;pull()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# initialize 新的 Node:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map[key] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Node(key, value)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;capacity &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;capacity &lt;span style="color:#f92672"&gt;-=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# capacity 不夠，要刪除 LRU&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; discard &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;tail&lt;span style="color:#f92672"&gt;.&lt;/span&gt;prev &lt;span style="color:#75715e"&gt;# 因為 capacity &amp;gt; 1，一定是非 dummy node&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; discard&lt;span style="color:#f92672"&gt;.&lt;/span&gt;pull() &lt;span style="color:#75715e"&gt;# 斷開連結！&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;del&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map[discard&lt;span style="color:#f92672"&gt;.&lt;/span&gt;key] &lt;span style="color:#75715e"&gt;# 在 Node Class 定義時記住的 key 派上用場了&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;del&lt;/span&gt; discard
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 當然，和 get 一樣，剛使用過/加入的 Node 也要放在「第一個」&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; node &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map[key]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; node&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;head&lt;span style="color:#f92672"&gt;.&lt;/span&gt;next)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;head&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(node)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="其他解法"&gt;其他解法&lt;/h2&gt;
&lt;h3 id="ordereddict"&gt;OrderedDict&lt;/h3&gt;
&lt;p&gt;對 Python 夠熟的人，也許會想到 &lt;code&gt;collections&lt;/code&gt; 中有個 &lt;code&gt;OrderedDict&lt;/code&gt;，key 會按照加入的順序排列，並提供了 &lt;code&gt;move_to_end&lt;/code&gt; 調整 key 順序，和 &lt;code&gt;popitem&lt;/code&gt; 來移除第一個 key。其實就是幫你把上面的方法包裝好了&amp;hellip;可以直接使用，雖然就失去了自己實作 LRU 的意義了（就像你用 Python List 實作 Stack 一樣，快但是沒有練習到）。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-Python" data-lang="Python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;LRUCache&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__init__&lt;/span&gt;(self, capacity: int):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;capacity &lt;span style="color:#f92672"&gt;=&lt;/span&gt; capacity
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map &lt;span style="color:#f92672"&gt;=&lt;/span&gt; collections&lt;span style="color:#f92672"&gt;.&lt;/span&gt;OrderedDict()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(self, key: int) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; int:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; key &lt;span style="color:#f92672"&gt;not&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; value &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map[key]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map&lt;span style="color:#f92672"&gt;.&lt;/span&gt;move_to_end(key)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; value
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;put&lt;/span&gt;(self, key: int, value: int) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; key &lt;span style="color:#f92672"&gt;in&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map[key] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; value
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map&lt;span style="color:#f92672"&gt;.&lt;/span&gt;move_to_end(key)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map[key] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; value
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;capacity &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;capacity &lt;span style="color:#f92672"&gt;-=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map&lt;span style="color:#f92672"&gt;.&lt;/span&gt;popitem(last&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# 移除第一個&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="dictionary"&gt;Dictionary&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Python 3.7 之後，Dictionary 保證了 key 會照著插入順序排列&lt;/strong&gt;。可以透過 O(1) 的 delete 和 insert 來重置 key 在 Dictionary 的順序，讓第一項是 priority 最低的項目，並且在 capacity 不足時，刪除第一個就好：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-Python" data-lang="Python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;LRUCache&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__init__&lt;/span&gt;(self, capacity: int):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;capacity &lt;span style="color:#f92672"&gt;=&lt;/span&gt; capacity
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(self, key: int) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; int:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; key &lt;span style="color:#f92672"&gt;not&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; value &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map[key]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;del&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map[key]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map[key] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; value
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; value
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;put&lt;/span&gt;(self, key: int, value: int) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; key &lt;span style="color:#f92672"&gt;in&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;del&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map[key]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map[key] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; value
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map[key] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; value
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;capacity &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;capacity &lt;span style="color:#f92672"&gt;-=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; item_view &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map&lt;span style="color:#f92672"&gt;.&lt;/span&gt;items()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; iterator &lt;span style="color:#f92672"&gt;=&lt;/span&gt; iter(item_view)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; key, _ &lt;span style="color:#f92672"&gt;=&lt;/span&gt; next(iterator)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;del&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;map[key]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;面試這樣寫可以證明你真的很熟 Python，但大概不容易給過吧 XD&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>聊聊 SQUID Token（魷魚遊戲幣）詐騙事件</title><link>https://dwye.dev/post/squid-token/</link><pubDate>Fri, 31 Dec 2021 15:01:56 +0800</pubDate><guid>https://dwye.dev/post/squid-token/</guid><description>
&lt;p&gt;&lt;img src="https://dwye.dev/img/SQUID/game2.jpg" alt="SQUID Token Website"&gt;&lt;/p&gt;
&lt;p&gt;這兩年（2020，2021）加密貨幣市值大漲，馬斯克之類的大企業家也開始關注加密貨幣發展。不過也發生了許多相關的詐騙案件，也讓外行人感到害怕，甚至會覺得加密貨幣是危險的，隨時會市值歸零。然而，雖然詐騙盛行，但加密貨幣本身只是一種技術，就像網路詐騙一樣，並不是整個網路都是詐騙，也不是所有加密貨幣都是詐騙。&lt;/p&gt;
&lt;p&gt;但也有是詐騙的加密貨幣。&lt;br&gt;
剛好最近和朋友聊到了 SQUID Token（魷魚遊戲幣，或是應該叫魷魚遊戲代幣，下稱 SQUID），這個代幣跟著《魷魚遊戲》一起爆紅，從 2021 年 10 月 20 日開始預售後秒殺，接著價格高速暴漲幾百倍，然後又在 2021 年 11 月 1 日高速歸零。&lt;/p&gt;
&lt;blockquote class="twitter-tweet"&gt;&lt;p lang="en" dir="ltr"&gt;Crypto shit coin rug pull &lt;a href="https://twitter.com/hashtag/SquidGameToken?src=hash&amp;amp;ref_src=twsrc%5Etfw"&gt;#SquidGameToken&lt;/a&gt; LIVE! &lt;a href="https://twitter.com/hashtag/shib?src=hash&amp;amp;ref_src=twsrc%5Etfw"&gt;#shib&lt;/a&gt; is next&lt;a href="https://twitter.com/search?q=%24squid&amp;amp;src=ctag&amp;amp;ref_src=twsrc%5Etfw"&gt;$squid&lt;/a&gt; &lt;a href="https://t.co/mdauJNfHvb"&gt;pic.twitter.com/mdauJNfHvb&lt;/a&gt;&lt;/p&gt;&amp;mdash; SimonZ (@SimonZawa) &lt;a href="https://twitter.com/SimonZawa/status/1455107386583232513?ref_src=twsrc%5Etfw"&gt;November 1, 2021&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;
&lt;blockquote&gt;
&lt;p&gt;Owww! It went to zero! Eeeooowwwwwww!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;他問我其他加密貨幣會不會也有這種風險，正好我其實對這件事的始末也沒有很了解，就趁機來研究一下這個 2021 下半年最有名的加密貨幣詐騙案。&lt;/p&gt;
&lt;h2 id="squid-token-是什麼"&gt;SQUID Token 是什麼？&lt;/h2&gt;
&lt;p&gt;魷魚遊戲幣，或著我覺得應該叫「代幣」比較合適，因為他並不是某個區塊鏈的原生貨幣（像是比特幣 / 以太幣 / Solana / BNB 等都是原生貨幣），而只是發行在幣安鏈（BSC）上的一個代幣（Token）。&lt;/p&gt;
&lt;p&gt;這個代幣的功用，就是作為進入開發商 squidgame.cash 所開發的 Squid Game 的入場門票。就像是去湯姆熊需要換代幣才能玩他們的機台一樣，SQUID 就是作為 Squid Game 遊玩時所需要的代幣：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/SQUID/game.jpg" alt="SQUID GameFi"&gt;&lt;/p&gt;
&lt;p&gt;不過因為還在草創期，這個代幣並沒有在一些知名的大型交易所發行，而是在所謂的去中心化交易所上架。&lt;/p&gt;
&lt;h3 id="去中心化交易所dex"&gt;去中心化交易所（DEX）&lt;/h3&gt;
&lt;p&gt;也就是 DeFi 的應用之一，純提供平台，讓用戶們可以自行創造市場，並藉由智能合約進行交易。由大量用戶提供幣（流動性）作為市場（流動性池），平台收取交易手續費，分潤給提供幣的用戶（流動性挖礦）。&lt;/p&gt;
&lt;p&gt;和中心化交易所相比，去中心化交易所是更自由的世界，上架一個幣並不需要太多的審核，也因此很多剛起步的小幣也都會從這開始。當然，更自由的代價也是更加危險，若買來的幣有問題，交易所不需要也不會負責。&lt;/p&gt;
&lt;h2 id="squid-token-的特殊設計"&gt;SQUID Token 的特殊設計&lt;/h2&gt;
&lt;h3 id="反傾銷設計"&gt;反傾銷設計&lt;/h3&gt;
&lt;p&gt;一般新聞都會寫，SQUID 的智能合約有著所謂的「反傾銷設計」：也就需要滿足特定條件才能交易賣出。這個條件如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;達到特定的解鎖日&lt;/li&gt;
&lt;li&gt;持有另一個 Marble Token。賣出越多 SQUID，所需要的 Marble 越多，而且每次賣出都會銷毀 Marble。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;也就是對於一般人來說，基本上就是買了就賣不出去了。&lt;br&gt;
也因為項目方限制了 SQUID 的賣出，就導致沒有賣壓，而買入的人看到價格飛漲，當然是先買再說，怕沒搭到車，買壓遠大於賣壓的情況下，就造成了新聞中說的三天暴漲 700 倍的結果。&lt;/p&gt;
&lt;h3 id="自我升級機制"&gt;自我升級機制&lt;/h3&gt;
&lt;p&gt;但一般來說，這種代幣的原始碼都是公開的，會在幣安鏈上被記錄。因此若有這種不能賣出的設計，一開始就會被幣安或是初期參與的人發現了。&lt;/p&gt;
&lt;p&gt;其實原先 SQUID 並沒有此設計，真正寫在 SQUID Token 中的設計，反而是一個看似無害的「自我升級機制」，這個機制讓他們可以偷偷換掉原本智能合約裡面的原始碼，變成別的東西。&lt;/p&gt;
&lt;p&gt;SQUID 開發者透過將 SQUID 自我升級引入「反傾銷設計」，防止其他人賣出。同時引入白名單，讓開發團隊可以免除於這個限制。他們可以不斷左手換右手，拉高幣價，讓其他人搶著買入。等到買入的單夠多了，開發者再透過短時間賣出等值於 250 萬美金的 SQUID，賺取一筆暴利，同時強大的瞬間賣壓也讓價格直接歸零（實際上是趨近於 0）。&lt;/p&gt;
&lt;p&gt;SQUID 的開發者掌握了市場，其他人都只是待割的韭菜。&lt;/p&gt;
&lt;h2 id="本身就是個詐騙"&gt;本身就是個詐騙&lt;/h2&gt;
&lt;p&gt;開發商 squidgame.cash 公布的團隊，看似來自許多知名大公司：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/SQUID/member.jpg" alt="squidgame.cash member"&gt;&lt;/p&gt;
&lt;p&gt;但後來慢慢有人發現，他們找不到這名單中任何一個人的資料。&lt;/p&gt;
&lt;p&gt;而他們宣稱的合作對象也一個一個否認他們與這個項目的相關。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/SQUID/partnership.jpg" alt="squidgame.cash partnership"&gt;&lt;/p&gt;
&lt;p&gt;綜合以上線索，這個 SQUID Token 打從一開始，就是個詐騙。&lt;/p&gt;
&lt;p&gt;只有一個人能獲勝，就像《魷魚遊戲》本身一樣，而那個人就是背後的開發者。&lt;/p&gt;
&lt;h2 id="其他的加密貨幣會不會也一夕歸零"&gt;其他的加密貨幣會不會也一夕歸零？&lt;/h2&gt;
&lt;p&gt;既然已經知道 SQUID 本身就是一筆詐騙案了，從合約本身開始就有問題，而會有人上鉤也是因為買的人不知道自己到底買了什麼，也就是所謂的「FOMO 仔」，很怕自己沒跟到這波所以先進去再說，然後就被割韭菜了。&lt;/p&gt;
&lt;p&gt;實際上大部分的加密貨幣都是算法上證實有效且安全的，如果真的害怕被小幣詐騙，還是盡量從大型中心化交易所買幣（像幣安），因為中心化交易所上架新幣是需要審核的，能夠通過審核的幣，雖然不一定會漲，但至少不會是詐騙。&lt;/p&gt;
&lt;p&gt;最大的風險，就是有一天人們不認同某個加密貨幣了，所有人瘋狂拋售該貨幣，導致價格大跌。但即便這天到來了，打倒該貨幣的也是真正的市場機制，是每個人，而不是開發者說歸零他就會歸零的，如果到了貨幣歸零那天你還持有著大量的該貨幣，那可能真的是投資失誤了 XD。就像買股票一樣，花錢買一個幣之前，先花點時間研究總是個好選項。&lt;/p&gt;
&lt;p&gt;實際上，真正的貨幣也是會有漲跌的，如果政府不當印鈔，或是貨幣失去了依靠的政府，那該貨幣也是會暴跌的，還記得&lt;a href="https://zh.wikipedia.org/wiki/%E8%88%8A%E8%87%BA%E5%B9%A3"&gt;舊台幣&lt;/a&gt;嗎？&lt;/p&gt;
&lt;h2 id="參考資料"&gt;參考資料&lt;/h2&gt;
&lt;p&gt;TechLead（一個國外的工程師 Youtuber）的講解十分詳細，這篇也大量參考他的解說：&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/zkBQF9QJLHY?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h3 id="幣圈基礎知識"&gt;幣圈基礎知識&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://genesisblockhk.com/zh/whats-a-token/"&gt;What&amp;rsquo;s a Token&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="squid-事件本身相關"&gt;SQUID 事件本身相關&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.cryptoratecap.com/crypto-kol/squid-game-token-invest/"&gt;魷魚遊戲代幣暴漲 Squid Game Token - CryptoRateCap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ptt.cc/bbs/DigiCurrency/M.1635753579.A.B37.html"&gt;魷魚幣飆漲3天翻700倍 財長：交易有賺就得課稅 - PTT 討論&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hypebeast.com/zh/2021/11/squid-game-cryptocurrency-collapses-scammers-make-off-with-two-million-usd-news"&gt;「SQUID 魷魚幣」開發商帶走約 $210 萬美金潛逃 - HYPEBEAST&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://news.tvbs.com.tw/world/1624321"&gt;魷魚遊戲同名虛擬幣是騙局？暴升再歸零變「水魚遊戲」&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="於-bsc-的位置"&gt;於 BSC 的位置&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;SQUID: &lt;a href="https://bscscan.com/token/0x87230146E138d3F296a9a77e497A2A83012e9Bc5"&gt;https://bscscan.com/token/0x87230146E138d3F296a9a77e497A2A83012e9Bc5&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Marble: &lt;a href="https://bscscan.com/token/0x9531c509a24CEEc710529645fc347341FF9F15EA"&gt;https://bscscan.com/token/0x9531c509a24CEEc710529645fc347341FF9F15EA&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>在 GitHub Action 優化 Node.js App 的環境建置</title><link>https://dwye.dev/post/github-action-npm-cache/</link><pubDate>Wed, 22 Dec 2021 20:41:23 +0800</pubDate><guid>https://dwye.dev/post/github-action-npm-cache/</guid><description>
&lt;p&gt;身為一位維護公司內部建置與發佈工具的工程師，建立方便又有效率的 CI/CD 工具來建置與測試產品，並且為產品的穩定性把關，一直是我們團隊的重要目標。不過我們對待自己的內部工具常常比產品來得隨便&amp;hellip;所以在我和主管聊過之後，決定慢慢幫我們的內部工具也來建立一些自動化的測試以及部署。&lt;/p&gt;
&lt;p&gt;內部工具說穿了就是個 Node.js 的應用程式。身為 YAML 工程師的我當然是用自己最熟悉的 GitHub Action 來實作。&lt;/p&gt;
&lt;h2 id="從最簡單的版本開始"&gt;從最簡單的版本開始&lt;/h2&gt;
&lt;p&gt;下面是一個最簡單的 Node.js 專案所使用的單元測試的 GitHub Action 的 YAML 設定：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Unit test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;on&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;push&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;jobs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;unit-test&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;runs-on&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ubuntu-latest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;steps&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;uses&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;actions/checkout@v2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;uses&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;actions/setup-node@v2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;node-version&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;16.13.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;npm install&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;npm run test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這份 YAML 簡單又白話。使用 &lt;code&gt;actions/checkout&lt;/code&gt; 拿到最新版的 Code，並使用 &lt;code&gt;actions/setup-node&lt;/code&gt; 設置 Node.js 環境。這兩個都是 GitHub 官方的 actions，簡單方便。然後使用 &lt;code&gt;npm install&lt;/code&gt; 指令安裝套件，接著跑我們自定義的測試腳本 &lt;code&gt;npm run test&lt;/code&gt;（註：記得換成你自己的測試指令）。&lt;/p&gt;
&lt;p&gt;其實如果不要要求太多，這樣就已經完成了，因此這次的分享就到這裡告一段落。&lt;/p&gt;
&lt;h2 id="還能做得更好嗎"&gt;還能做得更好嗎？&lt;/h2&gt;
&lt;p&gt;&amp;hellip;才怪，如果只是這樣，才不值得寫一篇文章 XD&lt;/p&gt;
&lt;p&gt;如果你的套件很多，每次都要跑 &lt;code&gt;npm install&lt;/code&gt; 會變成一個拖累你的 CI 速度的惡夢，測試如果不是簡單又快速，就會降低開發人員對於測試的關注，這絕對不是一件好事。&lt;/p&gt;
&lt;h2 id="優化環境建置速度"&gt;優化環境建置速度&lt;/h2&gt;
&lt;h3 id="actionssetup-node-的內建優化"&gt;Actions/setup-node 的內建優化&lt;/h3&gt;
&lt;p&gt;其實 &lt;code&gt;actions/setup-node&lt;/code&gt; 的文件就有寫到，他們有內建的 cache （快取）機制，可以透過將 &lt;code&gt;package-lock.json&lt;/code&gt; 或是 &lt;code&gt;yarn.lock&lt;/code&gt; 內的 dependency 做快取：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;uses&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;actions/setup-node@v2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;node-version&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;16.13.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;cache&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;npm&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;npm install&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;就是這麼簡單，更詳細用法可以查看 &lt;code&gt;actions/setup-node&lt;/code&gt; 的文件（&lt;a href="https://github.com/actions/setup-node"&gt;https://github.com/actions/setup-node&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;值得注意的是，這邊的 cache 對象是 &lt;code&gt;.npm&lt;/code&gt; 的資料夾，而不是 &lt;code&gt;node_modules&lt;/code&gt;，所以即便有 cache 了，在跑 &lt;code&gt;npm install&lt;/code&gt; 時還是有可能會需要下載與安裝一些 package。&lt;/p&gt;
&lt;h3 id="使用-npm-ci"&gt;使用 NPM CI&lt;/h3&gt;
&lt;p&gt;我也是在這次建置中才注意到這個酷功能的：&lt;br&gt;
&lt;a href="https://docs.npmjs.com/cli/v8/commands/npm-ci"&gt;https://docs.npmjs.com/cli/v8/commands/npm-ci&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;npm ci&lt;/code&gt; 顧名思義，是給 CI 用的 &lt;code&gt;npm install&lt;/code&gt;，專注於安裝套件的效率，跑起來比 &lt;code&gt;node_modules&lt;/code&gt; 快&lt;strong&gt;非常多&lt;/strong&gt;。加速的方法是透過避免掉一些平常在 &lt;code&gt;npm install&lt;/code&gt; 時會處理的套件相依問題，略過需要傳遞給開發者的訊息。&lt;code&gt;npm ci&lt;/code&gt; 唯一需要做的事，就是根據 &lt;code&gt;package-lock.json&lt;/code&gt; 安裝套件。&lt;/p&gt;
&lt;p&gt;我自己實測，可以把我們的專案的安裝時間從 3 分又幾秒降低到 45 秒附近，足足提升了 4 倍，真的誇張。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;uses&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;actions/setup-node@v2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;node-version&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;16.13.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;cache&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;npm&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;npm ci&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其實做到這邊就可以停了，但不禁還是想問，還能再更快嗎？&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/faster.jpg" alt="還要更快"&gt;&lt;/p&gt;
&lt;p&gt;總覺得每次跑 &lt;code&gt;npm ci&lt;/code&gt; 還要重新安裝一遍套件，重新建立 &lt;code&gt;node_modules&lt;/code&gt; 有點花時間。&lt;/p&gt;
&lt;h2 id="邪門加速法cache-node_modules"&gt;邪門加速法：Cache node_modules&lt;/h2&gt;
&lt;p&gt;大哉問：為何不直接 Cache node_modules 就好呢？&lt;/p&gt;
&lt;p&gt;可以參考前人的討論：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Actions/cache&lt;/code&gt; 的&lt;a href="https://github.com/actions/cache/issues/67"&gt;issue #67&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/42521884/"&gt;StackOverflow: Should I have Travis cache node_modules or $HOME/.npm&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我的理解是，當需要使用不同 Node.js / npm / OS 版本時，Cache node_modules 因為包含了一些針對特定環境編譯過的 packages，所以會在環境稍微不同時就造成問題。對 &lt;code&gt;.npm&lt;/code&gt; 做快取並不會讓 CI 速度變慢太多，但卻穩定很多，因此像是 GitHub Action / Travis 或 Azure 會預設對 &lt;code&gt;.npm&lt;/code&gt; 做快取，減少不懂的人遇到問題的機會 XD&lt;/p&gt;
&lt;p&gt;換個想法，如果我盡量使環境一致，把 Node.js 和 OS 版本都納入 cache key，環境有變動就讓 cache miss，這樣在大部分情況應該就能避免上述的問題，並且更加速 CI 的環境建置。&lt;/p&gt;
&lt;h3 id="npm-ci-的陷阱"&gt;NPM CI 的陷阱&lt;/h3&gt;
&lt;p&gt;但還有個問題： &lt;code&gt;npm ci&lt;/code&gt; 永遠都會先把 node_modules 刪掉再安裝一遍 packages（我想也是為了避免檢查目前已有的套件的正確性而做的優化）&lt;/p&gt;
&lt;p&gt;因此 &lt;code&gt;npm ci&lt;/code&gt; 不能和 cache node_modules 一起使用嗎？那就大錯特錯了 XD&lt;br&gt;
如果我們把 node_modules 快取起來，並且確認 &lt;code&gt;package-lock.json&lt;/code&gt; 和 node version 等因素都沒有改變，那就不必重新跑一遍 &lt;code&gt;npm ci&lt;/code&gt; 了啊，直接跑測試就行了。&lt;br&gt;
可以利用查看 node_modules 是否存在來判斷 cache hit 或 miss:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;test -d node_modules &lt;span style="color:#75715e"&gt;# 查看資料夾是否存在，把結果存入 $?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo $? &lt;span style="color:#75715e"&gt;# 0 代表存在，1 代表不存在&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="加入-cache-node_modules-並使用-npm-ci-後"&gt;加入 cache node_modules 並使用 npm ci 後：&lt;/h3&gt;
&lt;p&gt;因為變更有點多，這邊再次附上完整 YAML：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Unit test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;on&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;push&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;jobs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;unit-test&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;runs-on&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ubuntu-latest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;strategy&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;matrix&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;node-version&lt;/span&gt;: [&lt;span style="color:#ae81ff"&gt;16.13.1&lt;/span&gt;] &lt;span style="color:#75715e"&gt;# 把 node.js 版本記錄在這，之後可以重新取出作為 cache key&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;steps&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;uses&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;actions/checkout@v2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;uses&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;actions/setup-node@v2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;node-version&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;${{ matrix.node-version }}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Cache Node Modules&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;uses&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;actions/cache@v2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;path&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;node_modules&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# cache key 包含了所有我們希望固定的資訊：OS，node 版本，以及 package-lock.json 的檔案內容，使用 hashFiles 將檔案內容轉換成字串&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;key&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;node-modules-${{ runner.os }}-${{ matrix.node-version }}-${{ hashFiles(&amp;#39;package-lock.json&amp;#39;) }}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Install Packages&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;test -d node_modules &amp;amp;&amp;amp; echo &amp;#34;node_modules exists&amp;#34; || npm ci&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;npm run test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;透過 cache node_modules 後，建置時間大幅縮短，原本需要跑 3 分鐘才能建置完成的 CI，現在只需要 10 秒就可以跑完前置的環境建置部分了。即便是 cache miss，也能在 45 秒附近完成套件的安裝，可說是十分有效的優化，成就感十足。&lt;/p&gt;
&lt;p&gt;接下來要做的事情就是慢慢補上 unit test 了&amp;hellip;。&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>DeskMini X300 組裝紀錄</title><link>https://dwye.dev/post/deskmini-installation/</link><pubDate>Sat, 25 Sep 2021 17:42:23 +0800</pubDate><guid>https://dwye.dev/post/deskmini-installation/</guid><description>
&lt;h2 id="契機"&gt;契機&lt;/h2&gt;
&lt;p&gt;因為家母還在用我淘汰已久的舊電腦，有各種奇怪的問題，包含卡鍵或是滑鼠失靈、鍵盤連點之類的，對於電腦不太熟悉的家母來說要應付這些問題十分困難，而每次我都要出手去查出是什麼奇怪問題也很麻煩。&lt;/p&gt;
&lt;p&gt;也正很很久沒有組電腦了，就上參考 PTT 上原價屋抓菜單，後來發現華擎的 DeskMini 這個可以自己選配內部組件的迷你主機，就覺得蠻有趣的，整個配起來價格也可以接受，不會和市面上的迷你電腦差太多（有的文書機真的便宜的很誇張ˊˋ），性能需求也可以自己掌控，就下單買來組了。&lt;/p&gt;
&lt;p&gt;原本還懶惰想說交給原價屋組就好，只是原價屋那邊說，讓他們組裝的話，因為最近訂單多，要延後兩個禮拜才能出貨（是什麼換購電腦潮嗎，是說這時間點附近好像也是出生潮 XD），所以就選擇自己組裝了。&lt;/p&gt;
&lt;h2 id="菜單"&gt;菜單&lt;/h2&gt;
&lt;p&gt;姑且列一下選購內容：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;機殼/主機板/電供等: DeskMini X300&lt;/li&gt;
&lt;li&gt;CPU: AMD Athlon 3000G&lt;/li&gt;
&lt;li&gt;RAM: 金士頓 NB 8GB DDR4-3200 (KVR32S22S8/8)&lt;/li&gt;
&lt;li&gt;SSD: WD 藍標 SN550 500G/M.2 PCIe&lt;/li&gt;
&lt;li&gt;無線網卡: 華擎 DeskMini WiFi+BT4.2無線模組 (M.2 Key E 2230)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;總價 10460 元。&lt;/p&gt;
&lt;p&gt;除了 CPU 是 AMD 系列初階（但也已經有一定水平），RAM 和 SSD 其實並不是基礎水平。&lt;br&gt;
記憶體部分，我自己筆電 8G 記憶體也是略嫌不夠，覺得不選到 8G 會太小，而選擇 3200 而不是基本的 2666，則是我覺得價格沒差很多就順便升上去了&amp;hellip;（但這個順便其實意外的很花錢又是另一回事了 XD）。&lt;br&gt;
開機碟選擇 SSD 應該是現在的基本配備了，支援 M.2 就選 M.2，而 250 GB 和 500 GB 價格又沒差太多，所以就這樣選了，反正只是個 WD 藍標（結果總共好像比「順便」升了不少 XD）。&lt;/p&gt;
&lt;p&gt;無線網卡的部分是選購的，未雨綢繆，反正也 1000 有找，就直接先買一組，以後如果要接 wifi 或藍芽之類的也方便。&lt;/p&gt;
&lt;h2 id="組裝"&gt;組裝&lt;/h2&gt;
&lt;h3 id="開箱"&gt;開箱&lt;/h3&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/deskmini/before.jpg" alt="before"&gt;&lt;/p&gt;
&lt;p&gt;打開寄來的包裹，能看到的零組件就是這些，其實還蠻簡單的，因為 DeskMini 不需要自己選購電源供應器，也不需要太強大的負荷因此不用額外加裝散熱。&lt;/p&gt;
&lt;h3 id="cpu"&gt;CPU&lt;/h3&gt;
&lt;p&gt;組裝方式不用太煩惱，DeskMini 有附上蠻清楚的說明書，照著做就行了。因此第一個是裝上 CPU。AMD 的 CPU 我已經裝過好幾顆了（曾經壓壞一顆 CPU 的腳又是另一個故事了 ˊˋ），自然是熟能生巧：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/deskmini/cpu.jpg" alt="cpu"&gt;&lt;/p&gt;
&lt;p&gt;DeskMini 和 Athlon 都各附了一顆風扇，兩顆看起來基本上一模一樣。因為不是特別在意散熱，我就沒特別研究哪顆比較好，反正就挑一顆裝：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/deskmini/fan.jpg" alt="fan"&gt;&lt;/p&gt;
&lt;h3 id="ram"&gt;RAM&lt;/h3&gt;
&lt;p&gt;原本以為卡榫打開，壓下去就沒事了，結果發現壓了半天他都沒有卡進去。一度懷疑自己是不是買錯記憶體，但的確是筆電記憶體 SO-DIMM DDR4。直到我查到別篇在巴哈組裝別的電腦的文，也有人說記憶體插不進去，底下就有人說：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;大力出奇蹟&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;嗯，只要確定規格正確，插入方向正確，接下來就是不夠用力的問題了吧。&lt;/p&gt;
&lt;p&gt;接下來我就使出真的是吃奶的力氣，因為 Deskmini 的主機板的支撐在兩側，下方離底盤有段空隙，這樣壓讓我懷疑主機板會不會被我折斷，但記憶體還是卡在那。後來索性直接拿螺絲起子頂住主機版下方輔助，再次用力推，終於慢慢進去了。真的是大力出奇蹟。&lt;/p&gt;
&lt;p&gt;沒有插不進去，只是不夠大力。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/deskmini/memory.jpg" alt="memory"&gt;&lt;/p&gt;
&lt;p&gt;有人說 DeskMini 組裝簡單，但這步驟我還是花了我幾十分鐘嘗試和確認&amp;hellip;真的怕會把主機板或記憶體折斷ˊˋ。&lt;/p&gt;
&lt;h3 id="無線網路模組"&gt;無線網路模組&lt;/h3&gt;
&lt;p&gt;因為這張網卡要裝在 M.2 SSD 插槽底下，所以勢必要在 SSD 之前先裝上去。&lt;/p&gt;
&lt;p&gt;有個小訣竅是兩條天線模組可以先接到網卡，再把網卡裝到主機板，會比先裝網卡再接天線模組容易很多，畢竟主機板很卡手，不是很好處理小小的天線模組的接口。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/deskmini/wifi.jpg" alt="wifi module 1"&gt;&lt;/p&gt;
&lt;p&gt;天線模組的另一頭就是要接上天線本人。DeskMini 主機殼後方有幾個黑色圈圈可以取下，但也不是很容易取下（蠻緊的），我是先用螺絲起子硬戳下去，讓黑色圈圈半脫落，然後再用撬的把剩下的連接處撬開。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/deskmini/wifi2.jpg" alt="wifi module 2"&gt;&lt;/p&gt;
&lt;h3 id="ssd"&gt;SSD&lt;/h3&gt;
&lt;p&gt;最簡單的一部分，插進去、鎖螺絲，沒了。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/deskmini/ssd.jpg" alt="ssd"&gt;&lt;/p&gt;
&lt;h2 id="成品"&gt;成品&lt;/h2&gt;
&lt;p&gt;最後把主機板裝回去，天線鎖上，就大功告成了：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/deskmini/after1.jpg" alt="finish!"&gt;&lt;/p&gt;
&lt;p&gt;真的蠻迷你一台的。&lt;/p&gt;
&lt;p&gt;下面是完工後服役中的圖：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/deskmini/after2.jpg" alt="finish!"&gt;&lt;/p&gt;
&lt;h2 id="os"&gt;OS&lt;/h2&gt;
&lt;p&gt;作業系統的部分，因為已經不是學生了，沒辦法取得免費的 Windows 10，對於不太熟系電腦也不玩遊戲的家母來說，不需要 Windows 應該也沒關係吧？反正就讓她上網就可以了。&lt;/p&gt;
&lt;p&gt;所以我就大膽嘗試了 &lt;a href="https://linuxmint.com/"&gt;Linux Mint&lt;/a&gt; 作為作業系統，基於 ubuntu 並且有比較接近 Windows 系列的 UI，當然也支援繁體中文。&lt;/p&gt;
&lt;p&gt;當然，我也做了一些調整，把自動更新關掉，畫面字體調大，還有輸入法切換改成 shift 等等：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/deskmini/mint.jpg" alt="Mint"&gt;&lt;/p&gt;
&lt;p&gt;希望這台電腦可以解決一些我生活上的 &lt;a href="https://sre.google/sre-book/eliminating-toil/"&gt;Toil&lt;/a&gt;，SRE 精神嘛 XD&lt;br&gt;
組電腦也是一種用工程解決重複問題，大概吧。&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>Git Checkout / Switch / Restore 比較</title><link>https://dwye.dev/post/git-checkout-switch-restore/</link><pubDate>Tue, 14 Sep 2021 13:41:25 +0800</pubDate><guid>https://dwye.dev/post/git-checkout-switch-restore/</guid><description>
&lt;h2 id="前言"&gt;前言&lt;/h2&gt;
&lt;p&gt;git checkout 原本的功能有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;切換分支&lt;/li&gt;
&lt;li&gt;檔案管理&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;雖然兩者只是一個功能的一體兩面（從特定分支拿取特定檔案），但大多時候我們使用的時候會避免在一個指令進行太多邏輯操作，所以大多都只用到切換分支或是檔案管理其中一個。&lt;br&gt;
而且說實在，&lt;code&gt;checkout&lt;/code&gt; 一個檔案實在不是一個語意上很直觀的說法。&lt;/p&gt;
&lt;p&gt;因此在 2019 年底的 git 2.23 版本，釋出了兩個新指令：&lt;code&gt;git switch&lt;/code&gt; 和 &lt;code&gt;git restore&lt;/code&gt;，來切分 &lt;code&gt;git checkout&lt;/code&gt; 的龐大工作量。&lt;/p&gt;
&lt;p&gt;從名稱就可以大略知道：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;git switch&lt;/code&gt; 是用來切換分支&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git restore&lt;/code&gt; 是用來管理檔案&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="新舊指令對照"&gt;新舊指令對照&lt;/h2&gt;
&lt;h3 id="git-switch"&gt;Git Switch&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 切換分支&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git checkout &amp;lt;branch&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git switch &amp;lt;branch&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 強制切換分支（=切換 + reset --hard，丟棄所有變更）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git checkout -f &amp;lt;branch&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git switch -f &amp;lt;branch&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 切換 commit（d: detach，會讓 HEAD 進入到非分支的狀態）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git checkout &amp;lt;hash&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git switch -d &amp;lt;hash&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 創建分支，並切換過去&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git checkout -b &amp;lt;new_branch&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git switch -c &amp;lt;new_branch&amp;gt; &lt;span style="color:#75715e"&gt;# c for create&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="git-restore"&gt;Git Restore&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 復原工作區的檔案&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git checkout -- some_file
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git restore some_file
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git restore --worktree some_file &lt;span style="color:#75715e"&gt;# (a)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 取消 add，從暫存區移出&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git reset -- some_file
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git restore --staged -- some_file &lt;span style="color:#75715e"&gt;# (b)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 復原暫存區檔案&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git checkout HEAD -- some_file
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git restore --staged --worktree main test.txt &lt;span style="color:#75715e"&gt;# (a) + (b)，一次走兩步&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 從別的 commit 拿檔案&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git checkout &amp;lt;hash/branch&amp;gt; -- some_file
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git restore -s &amp;lt;hash/branch&amp;gt; some_file &lt;span style="color:#75715e"&gt;# s for source&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 互動式復原：-p&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git checkout -p -- some_file
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git restore -p some_file
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="小結"&gt;小結&lt;/h2&gt;
&lt;p&gt;可以發現新的兩個指令和 &lt;code&gt;checkout&lt;/code&gt; 比較大的差異分別是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;git switch&lt;/code&gt; 把對於特定 commit hash 的切換放進了 &lt;code&gt;-d&lt;/code&gt; option 內&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git restore&lt;/code&gt; 可以直接接受檔案作為 input，不用加上 &lt;code&gt;--&lt;/code&gt;，但代價就是指定分支時需要塞到 &lt;code&gt;-s&lt;/code&gt; 選項內&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;習慣使用 &lt;code&gt;checkout&lt;/code&gt; 的，可以繼續使用，但我覺得 &lt;code&gt;switch&lt;/code&gt; 和 &lt;code&gt;restore&lt;/code&gt; 蠻推薦給初學者使用的，學習門檻比複雜的 &lt;code&gt;checkout&lt;/code&gt; 本身低很多。&lt;/p&gt;
&lt;p&gt;（但其實一般初學者可能也只知道 &lt;code&gt;git checkout&lt;/code&gt; 的切換分支功能吧 XD）&lt;/p&gt;
&lt;h2 id="參考資料"&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://yanhaijing.com/git/2020/09/17/git-switch-and-restore/"&gt;Git 新命令 switch 和 restore&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.banterly.net/2021/07/31/new-in-git-switch-and-restore/"&gt;New in Git: switch and restore&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.csdn.net/Sweet_19BaBa/article/details/111950384"&gt;一次搞清 git checkout，git restore 和 git reset&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="官方文件"&gt;官方文件&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://git-scm.com/docs/git-checkout"&gt;git checkout&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git-scm.com/docs/git-switch"&gt;git switch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git-scm.com/docs/git-restore"&gt;git restore&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/git/git/blob/master/Documentation/RelNotes/2.23.0.txt"&gt;git v2.23.0 release note&lt;/a&gt;\&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>我的 Node.js Process 怎麼不會結束？</title><link>https://dwye.dev/post/node-event-loop-and-termination/</link><pubDate>Tue, 07 Sep 2021 16:13:25 +0800</pubDate><guid>https://dwye.dev/post/node-event-loop-and-termination/</guid><description>
&lt;p&gt;工作時碰到了某個以 Node.js 撰寫的 scheduled job（排程工作）一直保持在 running 的狀態，永遠不會進到 complete。這時一般首先想到的是，程式可能在某個地方卡住了，進入無窮等待（像是進入無限迴圈一樣）。&lt;/p&gt;
&lt;p&gt;一開始我試著使用懶人 debug 方法：加入幾個 &lt;code&gt;console.log&lt;/code&gt; 看看 code 是跑到哪裡停住了。&lt;br&gt;
結果，到程式碼最後一個 &lt;code&gt;console.log&lt;/code&gt; 也有被執行到。&lt;br&gt;
但程式依然沒有結束。&lt;/p&gt;
&lt;p&gt;如果你是個對 JavaScript 這個語言的運行機制很熟的人，馬上就知道這問題在哪了。&lt;/p&gt;
&lt;p&gt;但因為我在這之前我寫的都是其他語言，JavaScript 頂多看看語法差異就上陣了，也因此忽略了 JavaScript 一個很重要的特性：&lt;/p&gt;
&lt;h2 id="event-loop"&gt;Event Loop&lt;/h2&gt;
&lt;p&gt;Event Loop 其實就是 JavaScript 執行 Asynchronous 程式碼片段的方式：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/eventLoop.jpg" alt="simplified event loop"&gt;&lt;/p&gt;
&lt;p&gt;當 Node.js 執行程式時，會把主程式先執行完。這之間碰到 async code 的話，會將 &lt;strong&gt;async code 交給另外的 worker&lt;/strong&gt; 處理，並在 queue 註冊 callback，主程式則繼續往下進行。&lt;br&gt;
之後就會進入 Event Loop 不斷的監聽 callback，若有 worker 完成，則會將 callback 交給主程式執行。&lt;/p&gt;
&lt;p&gt;也因此，Node.js 的 async functions 不會 block 主程式進行，而會丟給背後的 worker 來跑。Worker 和主程式不一樣，&lt;strong&gt;可以是多個 Threads&lt;/strong&gt;，可以達到平行化，也因此 Node.js 鼓勵使用 asynchronous 的方式撰寫。&lt;/p&gt;
&lt;p&gt;註：這是我個人簡化後的版本，若有觀念上的錯誤歡迎指正。&lt;/p&gt;
&lt;h2 id="quick-example"&gt;Quick Example&lt;/h2&gt;
&lt;p&gt;這算是蠻經典的例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// index.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;setTimeout&lt;/span&gt;(() =&amp;gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;callback&amp;#39;&lt;/span&gt;), &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;end of code&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;❯ node index.js
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;end of code
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;callback
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;因為 &lt;code&gt;setTimeout&lt;/code&gt; 是 async，會註冊 callback，即便等待時間是 0，也會在主程式跑完後才執行。這也是我一開始 debug 的方法沒成功的原因。&lt;/p&gt;
&lt;h2 id="回到正題我的-nodejs-process-怎麼不會結束"&gt;回到正題：我的 Node.js Process 怎麼不會結束？&lt;/h2&gt;
&lt;p&gt;就是有 worker 還在工作，導致 &lt;strong&gt;event loop 一直在等 callback 可以被放進 call stack&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id="找出兇手"&gt;找出兇手&lt;/h3&gt;
&lt;p&gt;既然知道原因了，下一步就是找出是哪個 async code 沒有結束一直在等待。&lt;/p&gt;
&lt;p&gt;Node.js 有兩個一直沒有被官方列入文件的方法，可以快速查看現在還在 queue 等待的 work 有哪些：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;process&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;_getActiveHandles&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;process&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;_getActiveRequests&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這兩個底線開頭的方法直到最近還有 &lt;a href="https://github.com/nodejs/node/issues/36036"&gt;issue&lt;/a&gt; 在討論要不要加入文件，但看起來就是無限延長，畢竟這個方法印出的結果也是蠻冗長的，包含了很多給人類 debug 時不需要的資訊。&lt;/p&gt;
&lt;p&gt;因此我找到了一個 package：&lt;/p&gt;
&lt;h3 id="why-is-node-running"&gt;Why Is Node Running&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://github.com/mafintosh/why-is-node-running"&gt;why-is-node-running&lt;/a&gt; 是一個利用 Node.js 實驗中的 &lt;a href="https://nodejs.org/api/async_hooks.html"&gt;Async Hooks API&lt;/a&gt; 撰寫的 library，可以幫你調查你的 Node.js 程式為什麼不會結束。&lt;/p&gt;
&lt;p&gt;原理是在 &lt;code&gt;require&lt;/code&gt; 時，利用 side effects 註冊一些 Async Hooks，這樣當 async code 被執行時，這個 module 就會知道，也可以因此監聽有哪些 Code 沒結束。&lt;/p&gt;
&lt;p&gt;用法也蠻簡單的：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;log&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;require&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;why-is-node-running&amp;#39;&lt;/span&gt;) &lt;span style="color:#75715e"&gt;// 一定要一開始就 require，讓 side effects 優先執行
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;setInterval&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; () {}, &lt;span style="color:#ae81ff"&gt;1000&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;() &lt;span style="color:#75715e"&gt;// 印出目前還沒有結束的 work
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;❯ node index.js
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;There are 1 handle(s) keeping the process running
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# Timeout
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/Users/davidye/Projects/node_running/index.js:3 - setInterval(function () {}, 1000)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="參考資料"&gt;參考資料&lt;/h2&gt;
&lt;p&gt;實際上關於 Event Loop 還有很多細節，也有很多已經寫得不錯的文章，列在下方給大家做為參考。&lt;/p&gt;
&lt;h3 id="官方關於-event-loop-的說明文件"&gt;官方關於 Event Loop 的說明文件&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/"&gt;The Node.js Event Loop, Timers, and process.nextTick() | Node.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/en/docs/guides/dont-block-the-event-loop/"&gt;Don&amp;rsquo;t Block the Event Loop (or the Worker Pool)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nodejs.dev/learn/the-nodejs-event-loop"&gt;The Node.js Event Loop&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="event-loop-好文"&gt;Event Loop 好文&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.huli.tw/2019/10/04/javascript-async-sync-and-callback/"&gt;JavaScript 中的同步與非同步（上）：先成為 callback 大師吧！&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://notes.andywu.tw/2020/%E5%AE%8C%E6%95%B4%E5%9C%96%E8%A7%A3node-js%E7%9A%84event-loop%E4%BA%8B%E4%BB%B6%E8%BF%B4%E5%9C%88/"&gt;完整圖解 Node.js 的 Event Loop (事件迴圈)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yu-jack.github.io/2021/03/14/node-event-loop/"&gt;Event Loop 運行機制解析 - Node.js 篇&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.eebreakdown.com/2016/09/nodejs-eventemitter.html"&gt;非同步程式碼之霧：Node.js 的事件迴圈與 EventEmitter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="其他參考資料"&gt;其他參考資料&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/7698834"&gt;How does a node.js process know when to stop?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/17960452"&gt;How can I get a list of callbacks in the Node work queue? (or, Why won&amp;rsquo;t Node exit?)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>Git 小技巧</title><link>https://dwye.dev/post/git-tricks/</link><pubDate>Wed, 01 Sep 2021 13:41:25 +0800</pubDate><guid>https://dwye.dev/post/git-tricks/</guid><description>
&lt;h2 id="前言"&gt;前言&lt;/h2&gt;
&lt;h3 id="原來-git-比想像中的易學難精"&gt;原來 Git 比想像中的易學難精&lt;/h3&gt;
&lt;p&gt;剛開始學 git，我是隨便買了一本薄薄的書，然後跟著裡面的內容照著跑，就把 git 的基礎學起來了，例如說暫存區、分支、commit、tag、push 等等&lt;br&gt;
曾經我以為這樣就算學會 git 了，我也這樣用 git 用了一兩年，直到有一天在實習的公司被問到說&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;你會用 Git？那你會用 rebase 嗎？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;hellip;那是什麼？&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;不會 rebase 的話就不算會 Git 喔&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;所以趕快去把 rebase 學起來。&lt;br&gt;
哦哦，rebase 就是把一個分支搬到另一個分支上面，來讓歷史紀錄變得漂亮嘛，讓大家都像是從 develop 長出來的一樣。&lt;/p&gt;
&lt;p&gt;這下好了，我會 rebase 了，我總算能打遍天下無敵手了吧。&lt;/p&gt;
&lt;p&gt;後來到第二間公司實習，主管說，自己 commit 很醜沒關係，但你在推出去之前要把自己的 commit 修一下&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;要會用 rebase 修剪 commit，不然就不算是會 rebase&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;原來我 rebase 還沒學完嗎？&lt;/p&gt;
&lt;p&gt;因為第二間公司有圖書館，我就趁沒事的時候在圖書館逛逛有什麼書，就找到了好像近幾年蠻有名的「為你自己學 Git」，寫的淺顯易懂，即便是已經會一些 git 基本操作的人，也能夠學到一些東西，十分推薦給大家看（無業配）。當然還看了不少相關書籍與網路文章，感受到其實 git 有很多很方便的進階用法，也所以才有這篇文章。&lt;/p&gt;
&lt;h2 id="git-的檔案分區"&gt;Git 的檔案分區&lt;/h2&gt;
&lt;p&gt;雖然會使用指令，但其實一開始我並不了解每個階段的名稱，因此在此附上：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;工作目錄&lt;/li&gt;
&lt;li&gt;暫存區 Stage&lt;/li&gt;
&lt;li&gt;儲存庫 Repository&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;git add&lt;/code&gt; 從工作目錄把變更加到暫存區&lt;br&gt;
&lt;code&gt;git commit&lt;/code&gt; 提交，把暫存區的變更加入儲存庫，正式納入版本管理&lt;/p&gt;
&lt;h2 id="config"&gt;Config&lt;/h2&gt;
&lt;p&gt;常用的兩個 config 檔案：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--global&lt;/code&gt;：user 層級的，一般放在 &lt;code&gt;~/.gitconfig&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--local&lt;/code&gt;：repo 層級，放在 &lt;code&gt;.git/config&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;local 會優先&lt;/p&gt;
&lt;p&gt;使用 &lt;code&gt;git config -l&lt;/code&gt; 快速查看設定，也可以搭配 &lt;code&gt;--global&lt;/code&gt; 之類的 flag，只查看某層級的設定&lt;/p&gt;
&lt;h3 id="預設編輯器"&gt;預設編輯器&lt;/h3&gt;
&lt;p&gt;以 VSCode 為例：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;git config --global core.editor &amp;#34;code --wait&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;記得先裝 &lt;a href="https://code.visualstudio.com/docs/editor/command-line"&gt;VSCode Command Line Tool&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="在編輯器修改-config"&gt;在編輯器修改 config&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;git config -e&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;像我習慣把編輯器設定為 VSCode，就可以直接打開 &lt;code&gt;.git/config&lt;/code&gt;，很方便&lt;/p&gt;
&lt;h2 id="stage-相關操作"&gt;Stage 相關操作&lt;/h2&gt;
&lt;h3 id="加錯檔案"&gt;加錯檔案&lt;/h3&gt;
&lt;p&gt;如果用 &lt;code&gt;git rm&lt;/code&gt; 也會把檔案砍掉，因為其實 &lt;code&gt;git rm&lt;/code&gt; = &lt;code&gt;rm&lt;/code&gt; + &lt;code&gt;git add&lt;/code&gt;&lt;br&gt;
改用 &lt;code&gt;git rm --cached&lt;/code&gt; 就可以不砍掉檔案，單純移出暫存區&lt;/p&gt;
&lt;p&gt;同理&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git mv&lt;/code&gt; = &lt;code&gt;mv&lt;/code&gt; + &lt;code&gt;git add&lt;/code&gt; 前後兩個檔名&lt;/p&gt;
&lt;h3 id="不想要檔案變更回到修改前狀態"&gt;不想要檔案變更（回到修改前狀態）&lt;/h3&gt;
&lt;p&gt;用 &lt;code&gt;checkout&lt;/code&gt; 抵銷：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git checkout -- &amp;lt;file&amp;gt;&lt;/code&gt;：只復原工作區&lt;br&gt;
&lt;code&gt;git checkout HEAD -- &amp;lt;file&amp;gt;&lt;/code&gt;：連暫存區也復原，相當於 &lt;code&gt;git reset --hard -- &amp;lt;file&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;同理也可以拿取特定分支檔案&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git checkout &amp;lt;hash/branch&amp;gt; -- &amp;lt;file&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;同理，可以用 &lt;code&gt;git checkout &amp;lt;hash/branch&amp;gt; -f&lt;/code&gt; 硬切分支，會同時取消所有的變更！&lt;/p&gt;
&lt;h3 id="gitignore-忽略的檔案又想要加入版本控制"&gt;&lt;code&gt;.gitignore&lt;/code&gt; 忽略的檔案又想要加入版本控制&lt;/h3&gt;
&lt;p&gt;兩個方法：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;在 &lt;code&gt;.gitignore&lt;/code&gt; 內加入例外（反向）：&lt;code&gt;!filename&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;git add -f&lt;/code&gt; 強制加入（我忽略你的忽略，但不推薦）&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;可以用 &lt;code&gt;git clean -fx&lt;/code&gt; 一次清光不該被加入 stage 的檔案&lt;/p&gt;
&lt;h2 id="commit-相關情境"&gt;Commit 相關情境&lt;/h2&gt;
&lt;h3 id="修改上次-commit"&gt;修改上次 commit&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;git commit --amend&lt;/code&gt; 直接合併到上個 commit&lt;br&gt;
&lt;code&gt;git commit --amend --author &amp;quot;David Ye &amp;lt;david@dwye.dev&amp;gt;&amp;quot;&lt;/code&gt; 修改 commit 的作者&lt;/p&gt;
&lt;p&gt;或是直接使用 rebase 來修改也可以，通常用在需要修改比較前面的 commit，或是一次修改大量 commits&lt;/p&gt;
&lt;h3 id="拆掉-commit"&gt;拆掉 commit&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;git reset HEAD~&lt;/code&gt;&lt;br&gt;
&lt;code&gt;HEAD&lt;/code&gt; 是目前所在的 commit&lt;br&gt;
&lt;code&gt;~&lt;/code&gt; 代表往前走一個 commit，因此也可以用多個 &lt;code&gt;~&lt;/code&gt; 拆多個&lt;/p&gt;
&lt;h2 id="branch-操作"&gt;Branch 操作&lt;/h2&gt;
&lt;h3 id="新增-branch-同時切過去"&gt;新增 branch 同時切過去&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;git checkout -b &amp;lt;new_branch&amp;gt;&lt;/code&gt;，一步完成，就不需要先創建分支才切過去，使用頻率極高&lt;/p&gt;
&lt;h3 id="branch-名稱修改"&gt;branch 名稱修改&lt;/h3&gt;
&lt;p&gt;切到該分支，然後 &lt;code&gt;git branch -m &amp;lt;new_name&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="reset-與-checkout-的差別"&gt;Reset 與 Checkout 的差別&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;checkout&lt;/code&gt; 是移動現在的工作區到哪個 commit&lt;br&gt;
-&amp;gt; 移動工作區，分支放在地上&lt;br&gt;
&lt;code&gt;reset&lt;/code&gt; 是設定分支到某個地方，已經 commit 的其實不會動到&lt;br&gt;
-&amp;gt; 帶著分支移動&lt;/p&gt;
&lt;p&gt;所以 reset 可以拿來救 rebase！&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git reset ORIG_HEAD --hard
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;ORIG_HEAD&lt;/code&gt; 就是 rebase 操作前的原先 commit&lt;/p&gt;
&lt;h3 id="git-reset---hard-到底在幹嘛"&gt;&lt;code&gt;git reset --hard&lt;/code&gt; 到底在幹嘛&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;--hard&lt;/code&gt; 會把 staging 的檔案全部清掉，reset 預設是切到 HEAD，所以分支不動&lt;br&gt;
於是只有 &lt;code&gt;--hard&lt;/code&gt; 生效&lt;br&gt;
就像&lt;strong&gt;帶著分支&lt;/strong&gt;原地跳一下，把身上的東西抖掉的感覺 XD&lt;/p&gt;
&lt;h3 id="reset-可以當作-add-的相反"&gt;&lt;code&gt;reset&lt;/code&gt; 可以當作 &lt;code&gt;add&lt;/code&gt; 的相反&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;git reset -- &amp;lt;file&amp;gt;&lt;/code&gt; 取消 &lt;code&gt;add&lt;/code&gt;，變更會保留在工作區&lt;br&gt;
&lt;code&gt;git checkout -- &amp;lt;file&amp;gt;&lt;/code&gt; 連變更都移除掉！&lt;/p&gt;
&lt;h2 id="remote"&gt;Remote&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;git remote&lt;/code&gt; 列出節點（origin）&lt;br&gt;
&lt;code&gt;git remote --verbose&lt;/code&gt; 列出節點 / 網址 / fetch / push&lt;br&gt;
&lt;code&gt;git branch --all&lt;/code&gt; 可以看到遠端分支&lt;/p&gt;
&lt;h3 id="push"&gt;Push&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;git push -u origin master&lt;/code&gt; 設定上游分支！&lt;br&gt;
&lt;code&gt;git push origin master&lt;/code&gt; 只推送分支，不設定下次也會預設推到這&lt;br&gt;
&lt;code&gt;git push origin master:another_name&lt;/code&gt; 推到不同名分支&lt;/p&gt;
&lt;h3 id="刪除遠端分支"&gt;刪除遠端分支&lt;/h3&gt;
&lt;p&gt;其實就是推一個空分支上去&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git push origin :another_name&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="pull"&gt;Pull&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;pull&lt;/code&gt; = &lt;code&gt;fetch&lt;/code&gt; + &lt;code&gt;merge&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;fetch&lt;/code&gt;: 更新 remote 分支到本機&lt;br&gt;
&lt;code&gt;merge&lt;/code&gt;: 把本機分支和 remote 分支合併！&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pull --rebase&lt;/code&gt; = &lt;code&gt;fetch&lt;/code&gt; + &lt;code&gt;rebase&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="當你推分支推不上去"&gt;當你推分支推不上去&lt;/h3&gt;
&lt;p&gt;兩個方法：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;push -f&lt;/code&gt; 聽我的，強制更新遠端分支！&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pull --rebase&lt;/code&gt; 跟新自己的分支，然後 &lt;code&gt;push&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="rebase"&gt;Rebase&lt;/h2&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;git rebase &amp;lt;hash/branch&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;從 branch/hash &lt;strong&gt;重新開始長出分支&lt;/strong&gt;到現在的 commit&lt;/p&gt;
&lt;p&gt;操作完畢後，&lt;code&gt;ORIG_HEAD&lt;/code&gt; 會指向操作前的舊的 commit，雖然舊的 commit 會被藏起來，但其實舊的 commit 還是會存在一段時間的，因此可以用 &lt;code&gt;git reset --hard&lt;/code&gt; 返回。&lt;/p&gt;
&lt;p&gt;（當你害怕的時候，就用 &lt;code&gt;git rebase --abort&lt;/code&gt; 逃跑吧 XD）&lt;/p&gt;
&lt;h3 id="修改過去-i-的互動式修改"&gt;修改過去：&lt;code&gt;-i&lt;/code&gt; 的互動式修改&lt;/h3&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;git rebase -i &amp;lt;hash/branch&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;開啟互動模式，會顯示出所有 commit，上到下是舊到新&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;pick: 保留&lt;/li&gt;
&lt;li&gt;reword: 改 message&lt;/li&gt;
&lt;li&gt;squash: 把分支和&lt;strong&gt;上一個&lt;/strong&gt;壓扁&lt;/li&gt;
&lt;li&gt;fixup: 和 squash 相同，但 commit message 直接扔掉不保留&lt;/li&gt;
&lt;li&gt;edit: 停在這，回編輯器，等到 commit 後繼續，用來修改 commit
&lt;ul&gt;
&lt;li&gt;所以可以搭配 reset 拆 commit 內容：把檔案扔出暫存區，然後分次 commit&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;也可以換順序&lt;/p&gt;
&lt;p&gt;也能直接刪掉 commit，但不建議 &lt;strong&gt;（會有更動消失掉）&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;git rebase -i --autosquash &amp;lt;hash/branch&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;會把 commit message 是 &lt;code&gt;squash! &amp;lt;commit&amp;gt;&lt;/code&gt; 和 &lt;code&gt;fixup! &amp;lt;commit&amp;gt;&lt;/code&gt; 的 commit 分別自動標記成 &lt;code&gt;squash&lt;/code&gt; 和 &lt;code&gt;fixup&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;可以搭配 &lt;code&gt;git commit --fixup&lt;/code&gt; 使用，很方便&lt;/p&gt;
&lt;h2 id="worktree-一個-repo-多個目錄"&gt;worktree: 一個 repo 多個目錄&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;git worktree add &amp;lt;path&amp;gt; &amp;lt;branch&amp;gt;&lt;/code&gt; 設定分支為某個 path 底下的檔案&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git worktree list&lt;/code&gt; 列出 worktree&lt;/p&gt;
&lt;h3 id="刪除-worktree"&gt;刪除 worktree&lt;/h3&gt;
&lt;p&gt;先刪掉該資料夾（上面的 path），然後 &lt;code&gt;git worktree prune&lt;/code&gt;&lt;br&gt;
可以先 &lt;code&gt;git worktree prune -n&lt;/code&gt; 看看會刪掉 (dry run)&lt;/p&gt;
&lt;h2 id="submodule"&gt;submodule&lt;/h2&gt;
&lt;p&gt;執行 git clone 時不會自動把 submodule 一起 clone 過來，所以要初始化 submodule 並 clone：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;git submodule update --init --recursive
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;相關設定會存在 &lt;code&gt;.git/config&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="刪除-submodule"&gt;刪除 submodule&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;git submodule deinit &amp;lt;path&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git rm &amp;lt;path&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="更換-submodule-的-reference-url"&gt;更換 submodule 的 reference url&lt;/h3&gt;
&lt;p&gt;用指令更改：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;git config -f .gitmodules submodule.&amp;lt;name&amp;gt;.url &amp;lt;url&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;或手動更改 &lt;code&gt;.gitmodules&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;然後 sync：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;git submodule sync
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="其他技巧"&gt;其他技巧&lt;/h2&gt;
&lt;h3 id="log"&gt;Log&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;git log --oneline&lt;/code&gt; 可以只看一行的 commit message，比起一般的 &lt;code&gt;git log&lt;/code&gt; 我更常用這個&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git reflog&lt;/code&gt; 或 &lt;code&gt;git log -g&lt;/code&gt; 看 HEAD 移動，就像是你的操作記錄，想要回到某個 commit 卻忘了在哪，可以使用這個方法找到，包含回到 &lt;code&gt;rebase&lt;/code&gt; 之前的某個 commit 也行&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="tig"&gt;Tig&lt;/h3&gt;
&lt;p&gt;可以使用 &lt;code&gt;tig&lt;/code&gt; 這套工具，提供更簡明的 git 相關顯示以及互動式操作：&lt;br&gt;
&lt;a href="https://github.com/jonas/tig"&gt;https://github.com/jonas/tig&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/tig.png" alt="tig example"&gt;&lt;/p&gt;
&lt;h2 id="git-學習資源推薦"&gt;Git 學習資源推薦&lt;/h2&gt;
&lt;p&gt;同時也是本篇的參考資料&lt;/p&gt;
&lt;h3 id="免費電子書"&gt;免費電子書&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gitbook.tw/"&gt;為你自己學 Git&lt;/a&gt;：淺顯易懂的教學，大推&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zlargon.gitbooks.io/git-tutorial/content/"&gt;Git-Tutorials 基本使用教學&lt;/a&gt;：較精簡&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git-scm.com/book/zh-tw/v2"&gt;Pro Git&lt;/a&gt;：官方，前半有中譯&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="實體書籍"&gt;實體書籍&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.tenlong.com.tw/products/9789864766932?list_name=srh"&gt;完整學會 Git, GitHub, Git Server 的 24 堂課，2/e&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="其他參考資料"&gt;其他參考資料&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nvie.com/posts/a-successful-git-branching-model/"&gt;A successful Git branching model&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sethrobertson.github.io/GitBestPractices/"&gt;Git Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>客製化 git 使用的 SSH Key</title><link>https://dwye.dev/post/git-custom-ssh/</link><pubDate>Thu, 05 Aug 2021 15:33:25 +0800</pubDate><guid>https://dwye.dev/post/git-custom-ssh/</guid><description>
&lt;p&gt;寫完&lt;a href="https://dwye.dev/post/ssh-switch-key/"&gt;一個指令切換預設使用的 SSH Key&lt;/a&gt;這篇之後，又思考了一下有沒有其他方式來解決這個同個電腦中使用兩個 github 帳號的問題，畢竟那篇是採用 ssh-agent level 來解決，但其實搞不好也可以直接從 git 的 config 下手，因此這次重新用更多關鍵字去調查解法，發現 git 其實也支援客製化 ssh 指令。&lt;/p&gt;
&lt;h2 id="解法一環境變數-git_ssh_command"&gt;解法一：環境變數 &lt;code&gt;GIT_SSH_COMMAND&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;設定這個環境變數即可改變 git 使用的 ssh 指令。&lt;/p&gt;
&lt;p&gt;可以透過 &lt;code&gt;export&lt;/code&gt; 對當下的 shell 做一次性修改（也能達成一指令切換）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export GIT_SSH_COMMAND&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;ssh -o IdentitiesOnly=yes -i ~/.ssh/my_rsa&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;或是直接搭配 git 指令使用：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;GIT_SSH_COMMAND&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;ssh -o IdentitiesOnly=yes -i ~/.ssh/my_rsa&amp;#34;&lt;/span&gt; git push
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="-o-identitiesonlyyes-防止--i-的-key-被-ssh-agent-取代"&gt;&lt;code&gt;-o IdentitiesOnly=yes&lt;/code&gt;: 防止 &lt;code&gt;-i&lt;/code&gt; 的 Key 被 SSH Agent 取代&lt;/h3&gt;
&lt;p&gt;我自己也無法 100% 確定什麼時候 &lt;code&gt;-i&lt;/code&gt; Options 會無效，不過知道原因是因為即便是加了 &lt;code&gt;-i&lt;/code&gt; 指定 key file，還是會參照到 ssh agent 現存的所有 keys。因此還是加上 &lt;code&gt;-o IdentitiesOnly=yes&lt;/code&gt; 保險。&lt;/p&gt;
&lt;h2 id="解法二更改-git-configuration-的-coresshcommand"&gt;解法二：更改 Git Configuration 的 &lt;code&gt;core.sshCommand&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;根據文件，會影響到 &lt;code&gt;git fetch&lt;/code&gt; 和 &lt;code&gt;git push&lt;/code&gt; 時使用的 ssh 指令。&lt;/p&gt;
&lt;p&gt;因此可以在已經 clone 好的 repo 內下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git config --local core.sshCommand &lt;span style="color:#e6db74"&gt;&amp;#34;ssh -o IdentitiesOnly=yes -i ~/.ssh/my_rsa&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="解法三直接切換-ssh-agent"&gt;解法三：直接切換 SSH Agent&lt;/h2&gt;
&lt;p&gt;就是我原本的解法，詳見這篇：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dwye.dev/post/ssh-switch-key/"&gt;一個指令切換預設使用的 SSH Key&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="小結"&gt;小結&lt;/h2&gt;
&lt;p&gt;最後採用解法二 + 三，三用來切換 default 的 SSH Key 用於 clone 自己的 private repo 時使用，之後就在 repo 內直接設定 &lt;code&gt;core.sshCommand&lt;/code&gt; 就不用每次再重新切換 ssh key 了。&lt;/p&gt;
&lt;p&gt;順便附上我準備的設定 local gitconfig 的 script：&lt;/p&gt;
&lt;script src="https://gist.github.com/dwy6626/ce6bd08aad6ce17d0d277f21ba2c6a3f.js"&gt;&lt;/script&gt;
&lt;h2 id="參考資料"&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://superuser.com/questions/232373/"&gt;How to tell git which private key to use?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://superuser.com/questions/681877/ssh-ignores-i-switch"&gt;SSH ignores -i switch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git-scm.com/docs/git"&gt;git doc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git-scm.com/docs/git-config"&gt;git config doc&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>一個指令切換預設使用的 SSH Key</title><link>https://dwye.dev/post/ssh-switch-key/</link><pubDate>Tue, 03 Aug 2021 20:00:25 +0800</pubDate><guid>https://dwye.dev/post/ssh-switch-key/</guid><description>
&lt;p&gt;因應公司和個人有分開的 github 帳號，為了能在同一台電腦同使用者內工作，已經幾次重複解決這個問題，故寫篇文章記錄之。&lt;/p&gt;
&lt;h3 id="檢視現在使用的-ssh-key"&gt;檢視現在使用的 SSH Key&lt;/h3&gt;
&lt;p&gt;我們可以使用 &lt;code&gt;ssh-add&lt;/code&gt; 這個工具來檢視目前使用 &lt;code&gt;ssh&lt;/code&gt; 指令連線時，會使用的 ssh key：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ ssh-add -l
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;3072&lt;/span&gt; SHA256:******************/****************** my-default-email@dwye.dev &lt;span style="color:#f92672"&gt;(&lt;/span&gt;RSA&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;也可以直接用 &lt;code&gt;ssh -T&lt;/code&gt;，使用 ssh 連線但不要使用虛擬終端（pty）&lt;br&gt;
這也衍生了 github 使用 &lt;code&gt;git&lt;/code&gt; 這個 user，作為設置 ssh key 時常見的確認手段：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ ssh -T git@github.com
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hi your-account! You&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;ve successfully authenticated, but GitHub does not provide shell access.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="ssh-key-的切換"&gt;SSH Key 的切換&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ssh-add&lt;/code&gt; 顧名思義也可以用來管理 ssh-agent 現在使用的 ssh-key。&lt;/p&gt;
&lt;p&gt;加入一個 ssh key:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ ssh-add ~/.ssh/my_rsa
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Identity added: /Users/myaccount/.ssh/my_rsa &lt;span style="color:#f92672"&gt;(&lt;/span&gt;my-default-email@dwye.dev&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;清空 ssh key:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ ssh-add -D
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;All identities removed.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;因此我們就可以做如此操作達到切換 ssh key 的效果：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ~/switch_main.sh&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh-add -D
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh-add ~/.ssh/id_rsa
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ~/switch_personal.sh&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh-add -D
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh-add ~/.ssh/my_rsa
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然後在啟動 shell 時加入 alias 即可&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ~/.bashrc 或是 ~/.zshrc&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;alias gitp&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;source ~/switch_personal.sh&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;alias gitm&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;source ~/switch_main.sh&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;之後就可以在 terminal 中透過 &lt;code&gt;gitp&lt;/code&gt; 或 &lt;code&gt;gitm&lt;/code&gt; 一指令切換 ssh key 了。&lt;/p&gt;
&lt;h2 id="85-更新"&gt;8/5 更新&lt;/h2&gt;
&lt;p&gt;是的，才寫完這篇沒多久馬上就找到新解法來切換 git 使用的 ssh key，就不用每次開 shell 都要去手動改了（變成 0 指令切換 XD），我也比較喜歡這個新解法，詳細請見&lt;a href="https://dwye.dev/post/git-custom-ssh/"&gt;客製化 git 使用的 SSH Key&lt;/a&gt;&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>Ruby Object Model</title><link>https://dwye.dev/post/ruby-object-model/</link><pubDate>Sat, 17 Jul 2021 23:41:25 +0800</pubDate><guid>https://dwye.dev/post/ruby-object-model/</guid><description>
&lt;h2 id="前言ruby-中的物件導向"&gt;前言：Ruby 中的物件導向&lt;/h2&gt;
&lt;p&gt;Ruby Object Model 是我覺得 Ruby 算是十分特別也是十分重要的概念。&lt;/p&gt;
&lt;p&gt;在一般的物件導向程式語言中，物件是類別的實體，而資料型態是資料型態。一個變數可以儲存一個資料型態，一個變數也可以指向一個物件，但資料型態和物件是兩回事。&lt;/p&gt;
&lt;p&gt;但在 Ruby 中，&lt;strong&gt;所有的東西都是物件&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id="所有東西都是物件"&gt;所有東西都是物件&lt;/h3&gt;
&lt;p&gt;對，在 Ruby 中，主程序（Main）本身是一個物件，被定義好的 function 是物件，你所知道的資料型態如：整數、浮點、Boolean、字串（Ruby 中沒有字串與字元的分別）等是物件，就連平常代表虛無的 &lt;code&gt;nil&lt;/code&gt; 也是一個物件！&lt;/p&gt;
&lt;p&gt;打開 irb 試試看：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; :&lt;span style="color:#ae81ff"&gt;001&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; a &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; :&lt;span style="color:#ae81ff"&gt;002&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; a&lt;span style="color:#f92672"&gt;.&lt;/span&gt;nil?
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; :&lt;span style="color:#ae81ff"&gt;003&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;class
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NilClass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;因為 Ruby 中 &lt;code&gt;nil&lt;/code&gt; 是一個 &lt;code&gt;NilClass&lt;/code&gt; 的 instance（也是唯一的 instance，不能 new 一個出來），所以有 &lt;code&gt;nil?&lt;/code&gt; 這種方法，可以問一個變數是不是 &lt;code&gt;nil&lt;/code&gt;，夠神奇吧？&lt;br&gt;
這個語法糖相當於其他語言的 &lt;code&gt;a == nil&lt;/code&gt; 或是 &lt;code&gt;a is None&lt;/code&gt;，但變成 method 的形式讀起來更直觀。&lt;/p&gt;
&lt;h3 id="所有-functions-都是-methods"&gt;所有 Functions 都是 Methods&lt;/h3&gt;
&lt;p&gt;因為所有東西都是物件，在主程序以 &lt;code&gt;def&lt;/code&gt; 定義的 function，其實也只是掛在 Main object 下的 private method:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; :&lt;span style="color:#ae81ff"&gt;001&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;my_custom_method&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; :&lt;span style="color:#ae81ff"&gt;002&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; puts &lt;span style="color:#e6db74"&gt;&amp;#39;hello&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; :&lt;span style="color:#ae81ff"&gt;003&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;:my_custom_method&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 注意如果用 methods 只會拿到 public 和 protected methods&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 這邊的 self 其實是可以省略的，但為了強調我們是在存取 `main` 這個 object，先保留了：&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; :&lt;span style="color:#ae81ff"&gt;004&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;private_methods&lt;span style="color:#f92672"&gt;.&lt;/span&gt;include? &lt;span style="color:#e6db74"&gt;:my_custom_method&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;而 Main object 已經在使用了，還可以後續再寫入方法，這也是 ruby 的特色之一。&lt;/p&gt;
&lt;h2 id="ruby-object-model"&gt;Ruby Object Model&lt;/h2&gt;
&lt;p&gt;Ruby 的物件模型，也就是 Ruby 中物件導向的運作機制。&lt;/p&gt;
&lt;h3 id="instance"&gt;Instance&lt;/h3&gt;
&lt;p&gt;在 Ruby 中，可以利用 &lt;code&gt;Class#new&lt;/code&gt; 幫一個類別建立新的 instance，這時 instance 內儲存的資訊有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;instance_variable&lt;/li&gt;
&lt;li&gt;class&lt;/li&gt;
&lt;li&gt;object_id&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instance methods 不需要存在每個物件裡，只需要存在 class 並查找 instance methods 就好。&lt;/p&gt;
&lt;h3 id="self"&gt;Self&lt;/h3&gt;
&lt;p&gt;在 ruby 中，永遠都會有一個代表「現在的物件」，可以透過 &lt;code&gt;self&lt;/code&gt; 取得：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;puts self
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# =&amp;gt; main，在最外層，self 代表 main object&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Car&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; puts self
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# =&amp;gt; Car，在 class 內，self 是當下的 class object&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;run&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; puts &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;#{&lt;/span&gt;self&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; run! pupu!&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 在 method 內，self 是物件本身！&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;c &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Car&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;new
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;c&lt;span style="color:#f92672"&gt;.&lt;/span&gt;run &lt;span style="color:#75715e"&gt;# =&amp;gt; #&amp;lt;Car:0x00007fe17f8eeac8&amp;gt; run! pupu!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;而這邊還有個有趣的觀察：&lt;br&gt;
在 ruby 中，定義 class 是當下就會執行，而 def 定義 method 則是使用時動態執行。&lt;/p&gt;
&lt;h3 id="singleton-class"&gt;Singleton Class&lt;/h3&gt;
&lt;p&gt;Singleton Class 是跟著每個 ruby 物件自動產生，對於每個物件是&lt;strong&gt;獨一無二&lt;/strong&gt;的 Class。&lt;br&gt;
會有這樣的設計，要回到剛剛說的 ruby 的物件不會儲存 instance method 的特性。&lt;/p&gt;
&lt;p&gt;因為物件不存 method，但 ruby 又有個特色，可以為現有的物件定義新方法：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; c
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;run_twice&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; run
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; run
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;c&lt;span style="color:#f92672"&gt;.&lt;/span&gt;run_twice
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# #&amp;lt;Car:0x00007fe17f8eeac8&amp;gt; run! pupu!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# #&amp;lt;Car:0x00007fe17f8eeac8&amp;gt; run! pupu!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這時候這個 &lt;code&gt;run_twice&lt;/code&gt; 方法是只有 &lt;code&gt;c&lt;/code&gt; 這個物件擁有，其他的 &lt;code&gt;Car&lt;/code&gt; 的 instance 是不會擁有的，因此 &lt;code&gt;run_twice&lt;/code&gt; 不能被放在 &lt;code&gt;Car&lt;/code&gt; 這個 class 裡。&lt;/p&gt;
&lt;p&gt;在 ruby 中，這種單一物件特有的 method，就叫做 singleton method：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;c&lt;span style="color:#f92672"&gt;.&lt;/span&gt;singleton_methods
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# =&amp;gt; [:run_twice]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;而 singleton class 就是拿來儲存 singleton method 用的。&lt;/p&gt;
&lt;p&gt;因為 singleton class 是獨一無二的，所以有幾個限制：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不能建立新的 instance（畢竟要是獨一無二，才叫 singleton）&lt;/li&gt;
&lt;li&gt;不能被繼承（但這個有例外，就是 class 的 singleton class，會自動跟著 class 自己產生繼承鏈（見下方），只是仍然不能手動去繼承他，不然會扔出 &lt;code&gt;TypeError&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;對於一般 object （並不是 &lt;code&gt;Class&lt;/code&gt; 的 instance）來說，每個物件都有 singleton class，因此會在後面加上物件本身的識別，並繼承自他的 class：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;c&lt;span style="color:#f92672"&gt;.&lt;/span&gt;singleton_class
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# =&amp;gt; #&amp;lt;Class:#&amp;lt;Car:0x00007fe17f8eeac8&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;c&lt;span style="color:#f92672"&gt;.&lt;/span&gt;singleton_class&lt;span style="color:#f92672"&gt;.&lt;/span&gt;superclass &lt;span style="color:#f92672"&gt;==&lt;/span&gt; c&lt;span style="color:#f92672"&gt;.&lt;/span&gt;class
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# =&amp;gt; true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;對於一般 class（&lt;code&gt;Class&lt;/code&gt; 的 instance），因為 class 本身為為一，所以 singleton class 也是唯一的，並繼承自其 superclass 的 singleton class。&lt;br&gt;
也就是 class 的 singleton class 們是形成一條繼承鏈，直到最後繼承自 Class。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;Car&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;singleton_class&lt;span style="color:#f92672"&gt;.&lt;/span&gt;superclass &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Car&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;class
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# =&amp;gt; false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;Car&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;superclass&lt;span style="color:#f92672"&gt;.&lt;/span&gt;singleton_class &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Car&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;singleton_class&lt;span style="color:#f92672"&gt;.&lt;/span&gt;superclass
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# =&amp;gt; true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;Car&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;singleton_class&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ancestors
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# =&amp;gt; [#&amp;lt;Class:Car&amp;gt;, #&amp;lt;Class:Object&amp;gt;, #&amp;lt;Class:BasicObject&amp;gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Class, Module, Object, Kernel, BasicObject]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="receiver"&gt;Receiver&lt;/h3&gt;
&lt;p&gt;當我們呼叫一個 method，會需要有一個 receiver，使用 &lt;code&gt;receiver.method_name&lt;/code&gt; 的形式調用 method。&lt;br&gt;
以上面 &lt;code&gt;c.run&lt;/code&gt; 為例，這時的 receiver 就是 &lt;code&gt;c&lt;/code&gt;。&lt;br&gt;
如果沒有指定 receiver，預設會使用 &lt;code&gt;self&lt;/code&gt;，也就是當下的物件。&lt;/p&gt;
&lt;p&gt;而 Ruby 的 &lt;code&gt;private&lt;/code&gt; method 的定義，即是&lt;strong&gt;不能明確指定 receiver&lt;/strong&gt;，換句話說，就是只能透過 &lt;code&gt;self&lt;/code&gt; 來呼叫（當然如果你指定成 &lt;code&gt;self&lt;/code&gt; 還是會噴 error 的喔 XD）。&lt;br&gt;
不過 Ruby 這麼自由的語言，當然可以繞過去，透過 &lt;code&gt;receiver.send(:method_name)&lt;/code&gt; 的方式即可呼叫 private method。&lt;/p&gt;
&lt;p&gt;Ruby 的哲學就是把給開發者最大的自由，當然也需要開發者謹慎的使用。&lt;/p&gt;
&lt;h3 id="method-lookup"&gt;Method Lookup&lt;/h3&gt;
&lt;p&gt;當我們呼叫一個物件的 method，ruby 會去看該物件的 class 有沒有該方法定義。&lt;br&gt;
而 class 的查找順序為：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Singleton class&lt;/li&gt;
&lt;li&gt;繼承鏈中的 class 一路上找 (&lt;code&gt;Class#ancestors&lt;/code&gt; 可以拿出繼承鏈，不過會把 Module 也包含在內)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;而對於每個 class &lt;code&gt;C&lt;/code&gt;而言，再以以下順序查找：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://ruby-doc.org/core-3.0.2/doc/syntax/refinements_rdoc.html"&gt;Refinements&lt;/a&gt;，LIFO（後啟用，優先查找）&lt;/li&gt;
&lt;li&gt;prepended module&lt;/li&gt;
&lt;li&gt;自身定義的 methods&lt;/li&gt;
&lt;li&gt;included module&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;而每個 Refinements 內部也會依照 prepend -&amp;gt; 直接定義 -&amp;gt; include 的順序來查找。&lt;/p&gt;
&lt;p&gt;如果到了繼承鏈底部，都沒有找到該方法，會回到 singleton class 並重新查找 &lt;code&gt;method_missing&lt;/code&gt;（這也是透過 &lt;code&gt;method_missing&lt;/code&gt; 定義的 ghost method 會比較慢的原因：要爬兩次繼承鏈）。&lt;/p&gt;
&lt;p&gt;上面就是目前 Ruby 的整個 method 查找過程。&lt;/p&gt;
&lt;h4 id="整理一下可以分解成三層"&gt;整理一下，可以分解成三層：&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;class 層：根據 singleton class -&amp;gt; superclass -&amp;gt; superclass 的順序查找。&lt;/li&gt;
&lt;li&gt;Refinement 層：若有 Refinements，則依照 LIFO 順序查找，最後才是該 class 自己。&lt;/li&gt;
&lt;li&gt;最內層：prepend -&amp;gt; def -&amp;gt; include。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="簡化版本"&gt;簡化版本：&lt;/h4&gt;
&lt;p&gt;一般看到的是沒有 refinements 的版本，而且 refinements 我自己也不常用 XD，拿掉也比較簡單：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;singleton class -&amp;gt; superclass -&amp;gt; superclass &amp;hellip;&lt;/li&gt;
&lt;li&gt;prepend -&amp;gt; def -&amp;gt; include&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;所以一個 instance 的 method 組成，其實就是 &lt;strong&gt;singleton methods + 繼承鏈上的所有 instance methods + refinements 時定義的 methods&lt;/strong&gt;（當然，不包含用 &lt;code&gt;method_missing&lt;/code&gt; 動態處理的 ghost methods）。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 因為 refinement 的 method 無法透過 Kernel#methods 取得，因此先忽略&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 這裡示範了 singleton methods + 繼承鏈上的所有 instance methods = instance 的 methods&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;c&lt;span style="color:#f92672"&gt;.&lt;/span&gt;methods &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;c&lt;span style="color:#f92672"&gt;.&lt;/span&gt;singleton_class, &lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;Car&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ancestors&lt;span style="color:#f92672"&gt;].&lt;/span&gt;map { &lt;span style="color:#f92672"&gt;|&lt;/span&gt;a&lt;span style="color:#f92672"&gt;|&lt;/span&gt; a&lt;span style="color:#f92672"&gt;.&lt;/span&gt;instance_methods(&lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;) }&lt;span style="color:#f92672"&gt;.&lt;/span&gt;reduce(&lt;span style="color:#e6db74"&gt;:+&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# =&amp;gt; true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;附帶一提，當呼叫 class method 時，其實也是一樣的，因為在 ruby 中 class 也是個物件，所以也是先找 singleton class，然後隨著繼承鏈上找。&lt;/p&gt;
&lt;h3 id="例如"&gt;例如：&lt;/h3&gt;
&lt;p&gt;以下面這個 &lt;code&gt;MyClass&lt;/code&gt; 為例：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;MyClass&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;ParentClass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;instance &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;MyClass&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;new
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;class 與 superclass 分別為：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Class&lt;/th&gt;
&lt;th&gt;Superclass&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;instance&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MyClass&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;instance&lt;/code&gt; 的 singleton class&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Class&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MyClass&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MyClass&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Class&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ParentClass&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ParentClass&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Class&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Object&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Object&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Class&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BasicObject&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BasicObject&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Class&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nil&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;畫成圖：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt; BasicObject
^
|
Object
^
|
ParentClass
^
|
MyClass
^
|
instance -&amp;gt; singelton_class
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;（參考自 &lt;a href="https://gist.github.com/damien-roche/351bf4e7991449714533#one-more-step"&gt;https://gist.github.com/damien-roche/351bf4e7991449714533#one-more-step&lt;/a&gt; 的圖並小修改）&lt;/p&gt;
&lt;p&gt;所以有個 method lookup 的口訣是「往右，然後一直往上」，其實就是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;往右：instance 的 singleton_class&lt;/li&gt;
&lt;li&gt;往上：superclass -&amp;gt; superclass &amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="參考資料"&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Metaprogramming Ruby 2, Paolo Perrotta&lt;/li&gt;
&lt;li&gt;我同事 Anthony 大神的 &lt;a href="https://qoosuperman.github.io/article/2021-07-31-Ruby_Metaprogramming/"&gt;Metaprogramming Ruby 筆記&lt;/a&gt;，感謝他 XD&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/23848667/"&gt;Ruby method lookup path for an object&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gist.github.com/damien-roche/351bf4e7991449714533"&gt;A Primer on Ruby Method Lookup&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>傳說對決 桐人語音台詞 來源對照（第二彈）</title><link>https://dwye.dev/post/sao-kirito-aov-2/</link><pubDate>Mon, 05 Jul 2021 23:41:25 +0800</pubDate><guid>https://dwye.dev/post/sao-kirito-aov-2/</guid><description>
&lt;p&gt;身為一個業餘 SAO 粉來就該來發第二篇粉絲文。&lt;br&gt;
出處都是刀劍神域第三季，Alicization 篇，而且大部分都是爬塔篇的內容。&lt;br&gt;
找的方法就是打開訓練場聽台詞，把台詞打下來，然後 Google，再開動畫去裡面找。&lt;/p&gt;
&lt;h2 id="選角"&gt;選角&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/SAO2/kirito2.jpg" alt="kirito"&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;違う、戦うんじゃ無い。勝つんだ&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;不，不是要跟你對抗，而是要幹掉你&lt;br&gt;
出處：第 4 話 3:25，打大隻哥布林前的對話，大隻哥布林問說你認真敢跟我打嗎&lt;br&gt;
&lt;img src="https://dwye.dev/img/SAO2/katsu.jpg" alt="kirito"&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;世界を、そこに生きる人々を愛さない者に、支配者たる資格はない&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;不愛這個世界、不愛活在這世界的人民的人，沒有資格當一個掌權者&lt;br&gt;
出處：第 24 話 7:00，與最高司祭嘴砲&lt;br&gt;
&lt;img src="https://dwye.dev/img/SAO2/shihaishya.jpg" alt="kirito"&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;俺はキリト。剣士キリトだ&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我是桐人，劍士桐人&lt;br&gt;
出處：第 44 話 4:44，加百列問桐人是誰時，同時用黑色心念包圍桐人，桐人聽到了 UGO 的聲音後彈開黑色心念回答&lt;br&gt;
感謝網友艾澪補充&lt;br&gt;
&lt;img src="https://dwye.dev/img/SAO2/kiritoda.jpg" alt="kirito2"&gt;&lt;/p&gt;
&lt;h2 id="平常"&gt;平常&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;過程こそが重要なんだ。這い蹲って死ぬか、剣を握って死ぬかがね&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;過程才是最重要的，跪下而死、或拿著劍而死（是不同的）← 這邊後面省略掉了，日文常見的省略用法&lt;br&gt;
出處：第 24 話 0:16，與最高司祭嘴砲&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;刃に込められたものは、相手の魂にまで届く。俺はそう信じる！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;劍中的信念可以傳達到對方的靈魂那裡，我是這麼相信的！&lt;br&gt;
出處：第 21 話，6:01，桐人 vs 整合騎士 UGO&lt;br&gt;
&lt;img src="https://dwye.dev/img/SAO2/vs_ugo.jpg" alt="kirito vs ugo"&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;それじゃ足りないな。俺の愚かさを償うには&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;那樣可不夠啊，還不足以彌補我的愚蠢&lt;br&gt;
出處：第 24 話，0:50，與最高司祭嘴砲&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;それだけが、あらがうことだけが、俺が今ここにいる理由だからだ&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;因為只有如此和命運對抗，才是我此時此刻在這裡的原因&lt;br&gt;
出處：第 24 話 6:31，與最高司祭嘴砲&lt;br&gt;
&lt;img src="https://dwye.dev/img/SAO2/aragau.jpg" alt="kirito vs admin"&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;俺達もあいつらも、生身の人間なんだ。そんな絶対の善悪なんてもの、人間には決められないんだよ、きっと。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;無論是我們或是他們，全都是血肉之軀的人類，那種絕對的善惡，人類是絕對無法決定的。&lt;br&gt;
出處：第 16 話，桐人打敗整合騎士副騎士長法娜提歐後，擔心她的傷勢&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;考えることは、人間のいちばん強い力だ。どんな名剣、どんな秘奥義よりも強い&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;思考是人類最強大的力量，比任何的名劍或秘藏奧義都還要強大&lt;br&gt;
出處：第 9 話 15:10，跟兩個學妹解釋，比起遵守規定，更重要的是思考&lt;br&gt;
&lt;img src="https://dwye.dev/img/SAO2/think.jpg" alt="kirito say thinking"&gt;&lt;/p&gt;
&lt;h2 id="首殺"&gt;首殺&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;剣の重さが戦いを左右する&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;劍的重量會左右戰局&lt;br&gt;
出處：第 9 話 0:11，和 UGO 解釋劍術的強度&lt;br&gt;
&lt;img src="https://dwye.dev/img/SAO2/weight.jpg" alt="kirito reading"&gt;&lt;/p&gt;
&lt;h2 id="多殺"&gt;多殺&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;まだ戦う気のあるやつはかかってこい！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;還想打的傢伙儘管過來！&lt;br&gt;
出處：第 4 話 8:27，打贏大隻哥布林之後嗆退底下的小嘍囉&lt;br&gt;
&lt;img src="https://dwye.dev/img/SAO2/head.jpg" alt="kirito and goblin head"&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;俺だって、ここで、負けられないんだ！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我也是絕對不能在這裡輸掉！&lt;br&gt;
出處：第 8 話 7:01，和學院上屆號稱最強的渦羅用真劍對打，想起大家之後說絕對不能輸&lt;br&gt;
&lt;img src="https://dwye.dev/img/SAO2/asuna.jpg" alt="asuna smile"&gt;&lt;/p&gt;
&lt;h2 id="凱薩"&gt;凱薩&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;頼むぜ、俺に力を貸してくれ&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;拜託了，將力量借給我吧&lt;br&gt;
出處：第 21 話，4:10，桐人 vs 整合騎士 UGO&lt;/p&gt;
&lt;h2 id="復活"&gt;復活&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;あぁ、立つよ。お前のためなら、何度だって&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我會站起來的，為了你，無論幾次我都會站起來&lt;br&gt;
出處：第 24 話 6:09，UGO 給桐人血紅色的藍薔薇劍，鼓勵桐人站起來繼續對抗&lt;br&gt;
&lt;img src="https://dwye.dev/img/SAO2/stand.jpg" alt="kirito and U/GO"&gt;&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>傳說對決 桐人語音台詞 來源對照（第一彈）</title><link>https://dwye.dev/post/sao-kirito-aov/</link><pubDate>Sun, 04 Jul 2021 23:41:25 +0800</pubDate><guid>https://dwye.dev/post/sao-kirito-aov/</guid><description>
&lt;p&gt;身為一個業餘 SAO 粉來就該來發一篇粉絲文。也好久沒寫技術文以外的東西了，就來寫點興趣導向的東西吧。&lt;br&gt;
這邊語音出處基本上都是刀劍神域第一季，除了大招的之外（大概是第一季沒有符合的招式吧 XD）。&lt;br&gt;
找的方法就是打開訓練場聽台詞，把台詞打下來，然後 Google，再開動畫去裡面找。&lt;/p&gt;
&lt;h2 id="選角"&gt;選角&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/SAO/kirito1.jpg" alt="kirito"&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;必ず勝つ、勝ってこの世界を終わらせる&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我一定會贏的，打贏他並且終結這個世界&lt;br&gt;
出處：第 14 話，8:10，桐人在跟茅場決鬥前對亞絲娜說&lt;br&gt;
&lt;img src="https://dwye.dev/img/SAO/the_world.jpg" alt="kirito hug asuna"&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;君たちは、俺が絶対に守るから&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我一定會全力保護你們的&lt;br&gt;
出處：第 3 話，10:42，跟幸同床的桐人在心中表示絕對會保護他們（QAQ&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ビーター、いい呼び名だな、それ&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;封弊者，這叫法聽起來不錯嘛&lt;br&gt;
出處：第 2 話，20:20，打倒第一層 boss 後桐人被嘴封弊者&lt;br&gt;
&lt;img src="https://dwye.dev/img/SAO/beater.jpg" alt="beater kirito"&gt;&lt;/p&gt;
&lt;h2 id="平常"&gt;平常&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;俺は、俺は、生き延びて見せる、この世界で&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我，我啊，一定會活下去的，在這個世界&lt;br&gt;
出處：第 1 話，21:28，桐人和克萊因道別後，一個人率先衝去隔壁城鎮的途中說的（同時也使出了 Rage Spike）&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;このゲームは遊びじゃないだ&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;這遊戲可不是鬧著玩的&lt;br&gt;
出處：第 4 話 9:03，桐人跟西莉卡解釋紅色玩家&lt;br&gt;
&lt;img src="https://dwye.dev/img/SAO/game.jpg" alt="kirito eat with silica"&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;君は、何があろうと帰してみせる、あの世界に&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;無論發生什麼事，我都會把你送回去的，回到現實世界裡&lt;br&gt;
出處：第 10 話，紅色殺意 18:25，桐人差點被亞絲娜的前護衛幹掉，事情解決之後的對話&lt;br&gt;
&lt;img src="https://dwye.dev/img/SAO/kiss.jpg" alt="kirito kiss asuna"&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;たかが数字が増えるだけで無茶な差が着く。それがレベル制MMOの理不尽さなんだ&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;只是增加數字就能拉大實力的差距，這就是等級制 MMO RPG 不合理的地方&lt;br&gt;
出處：第 4 話 19:44，桐人站著給橘色公會的人打&lt;br&gt;
&lt;img src="https://dwye.dev/img/SAO/stand_kirito.jpg" alt="kirito not hurt"&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;誰かを見殺しにするくらいなら、一緒に死んだ方がずっとマシだ&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;與其見死不救，不如一起死去比較好吧&lt;br&gt;
出處：第 7 話，心的溫度 11:19，莉茲貝特問桐人為何要冒風險救她&lt;/p&gt;
&lt;h2 id="首殺"&gt;首殺&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;これはデュエルじゃない、単純な殺し合いだ&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;這不是決鬥，只是單純的互相殺害&lt;br&gt;
出處：第 14 話，9:55，75 層打完 boss 後，和茅場單挑&lt;br&gt;
&lt;img src="https://dwye.dev/img/SAO/koroshiai.jpg" alt="kirito and kayaba"&gt;&lt;/p&gt;
&lt;h2 id="多殺"&gt;多殺&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;俺はビーターだ、元テスター如きと一緒にしないでくれ&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我就是封弊者，不要把我和那些封測玩家相提並論&lt;br&gt;
出處：第 2 話，20:26，打倒第一層 boss 後桐人被嘴封弊者&lt;/p&gt;
&lt;h2 id="死亡"&gt;死亡&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;ごめん、君だけは生きて&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;抱歉了，至少你要好好活下去&lt;br&gt;
出處：第 14 話，11:01，桐人被茅場誘導使出 Eclipse ，而認知到自己將輸掉決鬥的時候&lt;br&gt;
&lt;img src="https://dwye.dev/img/SAO/eclipse.jpg" alt="eclipse"&gt;&lt;/p&gt;
&lt;h2 id="復活"&gt;復活&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;この世界での強さは、単なる幻想に過ぎない。そんなものより、もっと大事なものがある&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在這個世界再如何強大，也只不過是幻想而已。比起這些，還有更重要的事物。&lt;br&gt;
出處：第 4 話 21:05，桐人安慰西莉卡的等級差距&lt;br&gt;
&lt;img src="https://dwye.dev/img/SAO/strength.jpg" alt="kirito and silica and bed"&gt;&lt;/p&gt;
&lt;h2 id="技能"&gt;技能&lt;/h2&gt;
&lt;p&gt;以一二三技能排序：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;スターバースト・ストリーム&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Starburst Stream / 星光流連擊（星爆氣流斬）&lt;br&gt;
第 9 話，13:45 開始，不解釋&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;レイジ・スパイク&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Rage Spike / 憤怒刺擊&lt;br&gt;
單手劍突進技&lt;br&gt;
第 1 話，21:30 開始&lt;br&gt;
&lt;img src="https://dwye.dev/img/SAO/rage_spike.jpg" alt="rage spike"&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ライトニング・フォール&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Lightning Fall / 閃電墜擊&lt;br&gt;
單手劍廣範圍攻擊技&lt;br&gt;
出處：第二季第 17 話，聖劍篇，桐人對寶物堆使用，用來找出雷屬性的槌子（動畫沒有喊出招式名&amp;hellip;這個超難找）&lt;br&gt;
&lt;img src="https://dwye.dev/img/SAO/ligntning_fall.jpg" alt="lightning fall"&gt;&lt;/p&gt;
&lt;blockquote class="twitter-tweet"&gt;&lt;p lang="ja" dir="ltr"&gt;キリトが使ったのは《ライトニング・フォール》というソードスキルで、真下への物理ダメージに加えて、周囲にスタン効果のある範囲攻撃を行うという技です。 &lt;a href="https://twitter.com/hashtag/sao2_jk?src=hash&amp;amp;ref_src=twsrc%5Etfw"&gt;#sao2_jk&lt;/a&gt;&lt;/p&gt;&amp;mdash; 川原礫;SAOP8巻6月発売 (@kunori) &lt;a href="https://twitter.com/kunori/status/528910909079425024?ref_src=twsrc%5Etfw"&gt;November 2, 2014&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;
&lt;p&gt;根據川原自己的描述和小說內容，是從高處落下，對下方使用的廣範圍雷屬性 + 物理攻擊&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>Rake Tasks 進階：Invoke, Execute, Enhance</title><link>https://dwye.dev/post/rake-execute-invoke-enhance/</link><pubDate>Thu, 01 Jul 2021 13:41:25 +0800</pubDate><guid>https://dwye.dev/post/rake-execute-invoke-enhance/</guid><description>
&lt;p&gt;在「&lt;a href="https://dwye.dev/post/rake/"&gt;如何在 Rails 中寫 Rake Tasks&lt;/a&gt;」一文中，已經紀錄了在 Ruby on Rails 中 Rake Task 的基本寫法。&lt;/p&gt;
&lt;p&gt;這邊要來談一些最近碰到的議題：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如何直接在一個 rake task 中呼叫另一個 rake task？&lt;/li&gt;
&lt;li&gt;或是如何在一個 rake task 執行時，做額外的事情？&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="在程式中呼叫-rake-task-invoke-與-execute"&gt;在程式中呼叫 Rake Task: Invoke 與 Execute&lt;/h2&gt;
&lt;p&gt;如果我們想要在 ruby 的程式中執行一個 rake task，可以使用 &lt;code&gt;Rake::Task['rake::name'].invoke&lt;/code&gt;，或是 &lt;code&gt;Rake::Task['rake::name'].execute&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id="invoke-與-execute-的差別"&gt;Invoke 與 Execute 的差別&lt;/h3&gt;
&lt;p&gt;一般執行 Rake task 時，會先 invoke 該 task，然後依序 invoke 其 dependencies，然後 execute dependencies，接著才 execute 我們從外部 invoke 的 task。&lt;/p&gt;
&lt;p&gt;也因此兩者的差別在於：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;invoke&lt;/code&gt; 如同在 command line 呼叫，完整的拉起 dependencies，並且每個 task 只會執行一次。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;execute&lt;/code&gt; 直接執行該 task 內容，不管該 task 有沒有被執行過。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="寫點-code-做實驗"&gt;寫點 Code 做實驗&lt;/h3&gt;
&lt;p&gt;我們利用「&lt;a href="https://dwye.dev/post/rake/"&gt;如何在 Rails 中寫 Rake Tasks&lt;/a&gt;」中定義好的 Task &lt;code&gt;hello:world&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;namespace &lt;span style="color:#e6db74"&gt;:hello&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; desc &lt;span style="color:#e6db74"&gt;&amp;#39;print hello world&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; task &lt;span style="color:#e6db74"&gt;world&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;%w[hello:man hello:earth]&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; puts &lt;span style="color:#e6db74"&gt;&amp;#39;Hello, world!&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; task &lt;span style="color:#e6db74"&gt;earth&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;%w[hello:man]&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; puts &lt;span style="color:#e6db74"&gt;&amp;#39;Hello, Earth~&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; task &lt;span style="color:#e6db74"&gt;:man&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; puts &lt;span style="color:#e6db74"&gt;&amp;#39;Hello, man.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;另外再加上兩個 tasks，分別用上了 &lt;code&gt;invoke&lt;/code&gt; 和 &lt;code&gt;execute&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;task &lt;span style="color:#e6db74"&gt;:invoke&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;Rake&lt;/span&gt;&lt;span style="color:#f92672"&gt;::&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;Task&lt;/span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;hello:world&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;].&lt;/span&gt;invoke
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;task &lt;span style="color:#e6db74"&gt;:execute&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;Rake&lt;/span&gt;&lt;span style="color:#f92672"&gt;::&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;Task&lt;/span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;hello:world&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;].&lt;/span&gt;execute
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;分別執行這兩個 tasks:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ bundle exec rake invoke
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, man.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, Earth~
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, world!
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ bundle exec rake execute
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, world!
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以發現 execute &lt;code&gt;hello:world&lt;/code&gt; 這個 task，並不會跟著跑他的 dependencies &lt;code&gt;hello:man&lt;/code&gt; 和 &lt;code&gt;hello:earth&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="更多實驗"&gt;更多實驗&lt;/h3&gt;
&lt;p&gt;rake 其實是可以一次 invoke 多個的，例如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bundle exec rake db:create db:migrate db:seed
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;所以我們也可以分別將剛剛的 &lt;code&gt;invoke&lt;/code&gt; 和 &lt;code&gt;excute&lt;/code&gt; 排列組合一下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ bundle exec rake execute invoke
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, world!
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, man.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, Earth~
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, world!
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ bundle exec rake invoke execute
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, man.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, Earth~
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, world!
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, world!
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ bundle exec rake invoke invoke
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, man.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, Earth~
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, world!
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ bundle exec rake execute execute
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, world!
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, world!
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;前兩種組合的結果告訴我們，先 &lt;code&gt;execute&lt;/code&gt; 一次，該 task 下次 &lt;code&gt;invoke&lt;/code&gt; 時還是會照常被執行。因此 &lt;code&gt;execute&lt;/code&gt; 對於需要直接執行該 task 的 block 裡面的內容是很好用的。&lt;br&gt;
而 &lt;code&gt;invoke&lt;/code&gt; 則是要考慮到之前是不是有被 &lt;code&gt;invoke&lt;/code&gt; 過，如果已經被 &lt;code&gt;invoke&lt;/code&gt; 過，即便再次 &lt;code&gt;invoke&lt;/code&gt; 一次，之後就不會再重複 &lt;code&gt;execute&lt;/code&gt; 了&lt;/p&gt;
&lt;h3 id="如何重複-invoke-一個-task"&gt;如何重複 invoke 一個 task？&lt;/h3&gt;
&lt;p&gt;使用 &lt;code&gt;reenable&lt;/code&gt; 這個方法即可：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;task &lt;span style="color:#e6db74"&gt;:reenable&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; puts &lt;span style="color:#e6db74"&gt;&amp;#39;Reenable!&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# because we invoke `invoke` to invoke `hello:world`, we need to re-enable both&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;Rake&lt;/span&gt;&lt;span style="color:#f92672"&gt;::&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;Task&lt;/span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;invoke&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;].&lt;/span&gt;reenable
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;Rake&lt;/span&gt;&lt;span style="color:#f92672"&gt;::&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;Task&lt;/span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;hello:world&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;].&lt;/span&gt;reenable
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這樣下次跑 &lt;code&gt;invoke&lt;/code&gt; 就會重複印出 &lt;code&gt;Hello, world!&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ bundle exec rake invoke reenable invoke
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, man.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, Earth~
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, world!
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Reenable!
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, world!
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="enhance幫-rake-task-打補丁"&gt;Enhance：幫 Rake Task 打補丁&lt;/h2&gt;
&lt;p&gt;如果你需要在執行特定的 rake task 前後做特定的事，那使用 &lt;code&gt;enhance&lt;/code&gt; 是不錯的選擇。&lt;code&gt;enhance&lt;/code&gt; 就像是在某個 task 加裝 hook 一樣，執行到該 task 時，就會執行他被 &lt;code&gt;enhance&lt;/code&gt; 的內容。&lt;/p&gt;
&lt;p&gt;一個例子是 &lt;a href="https://github.com/ctran/annotate_models"&gt;annotate_models&lt;/a&gt; 這個 gem，可以在 database migration 後，對 model 檔案加上註解，翻開其原始碼，果然是用 &lt;code&gt;enhance&lt;/code&gt; 實現的：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# https://github.com/ctran/annotate_models/blob/v3.1.1/lib/tasks/annotate_models_migrate.rake&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;%w(db:migrate db:migrate:up db:migrate:down db:migrate:reset db:migrate:redo db:rollback)&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;each &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt;task&lt;span style="color:#f92672"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;Rake&lt;/span&gt;&lt;span style="color:#f92672"&gt;::&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;Task&lt;/span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;task&lt;span style="color:#f92672"&gt;].&lt;/span&gt;enhance &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;Rake&lt;/span&gt;&lt;span style="color:#f92672"&gt;::&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;Task&lt;/span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;Rake&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;application&lt;span style="color:#f92672"&gt;.&lt;/span&gt;top_level_tasks&lt;span style="color:#f92672"&gt;.&lt;/span&gt;last&lt;span style="color:#f92672"&gt;].&lt;/span&gt;enhance &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; annotation_options_task &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Rake&lt;/span&gt;&lt;span style="color:#f92672"&gt;::&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;Task&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;task_defined?(&lt;span style="color:#e6db74"&gt;&amp;#39;app:set_annotation_options&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;app:set_annotation_options&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;set_annotation_options&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;Rake&lt;/span&gt;&lt;span style="color:#f92672"&gt;::&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;Task&lt;/span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;annotation_options_task&lt;span style="color:#f92672"&gt;].&lt;/span&gt;invoke
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;Annotate&lt;/span&gt;&lt;span style="color:#f92672"&gt;::&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;Migration&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;update_annotations
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這樣寫之後，當「執行完」上面所窮舉跟 database migration 有關的 rake task 時，就會 block 內的 code。&lt;/p&gt;
&lt;p&gt;那如果要在「執行前」插入 code 呢？&lt;/p&gt;
&lt;p&gt;&lt;code&gt;enhance&lt;/code&gt; 是可以額外增加 dependencies 的，可以將執行前要插入的 code 寫成另一個 rake task，然後將其定義為 &lt;code&gt;enhance&lt;/code&gt; 的 dependencies 即可：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;task &lt;span style="color:#e6db74"&gt;:before&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; puts &lt;span style="color:#e6db74"&gt;&amp;#39;before task&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 這裡 dependencies 一定要是 array，不然會報錯&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;Rake&lt;/span&gt;&lt;span style="color:#f92672"&gt;::&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;Task&lt;/span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;hello:world&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;].&lt;/span&gt;enhance(&lt;span style="color:#e6db74"&gt;%w[before]&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; puts &lt;span style="color:#e6db74"&gt;&amp;#39;after task&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;接著重跑一次 &lt;code&gt;invoke&lt;/code&gt; 這個 task:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ bundle exec rake invoke
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, man.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, Earth~
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;before task
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, world!
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;after task
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以發現 &lt;code&gt;enhance&lt;/code&gt; 的 dependency 會接在原先定義的 dependencies 後面。&lt;/p&gt;
&lt;h3 id="如果-execute-一個被-enhance-的-task-會發生什麼事"&gt;如果 Execute 一個被 Enhance 的 Task 會發生什麼事？&lt;/h3&gt;
&lt;p&gt;前面有討論到，如果我們直接 &lt;code&gt;execute&lt;/code&gt; 一個 task，其 dependencies 都會被忽略。而 &lt;code&gt;enhance&lt;/code&gt; 也包含了定義 dependencies 和後方的 block 的部分。揪竟 &lt;code&gt;enhance&lt;/code&gt; 一個 task 會不會對其被 &lt;code&gt;execute&lt;/code&gt; 時產生影響呢？&lt;/p&gt;
&lt;p&gt;實驗一次就會知道了：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ bundle exec rake execute
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, world!
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;after task
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;enhance&lt;/code&gt; 的 dependencies 也被扔掉了。&lt;br&gt;
然而其 block 內定義的 code 仍然會執行到。&lt;/p&gt;
&lt;p&gt;這個行為算是我覺得比較容易搞混的地方，老實說 &lt;code&gt;enhance&lt;/code&gt; 給人一種 monkey patch 的感覺，我個人是認為需要謹慎使用。&lt;/p&gt;
&lt;h3 id="enhance-多次的影響"&gt;Enhance 多次的影響&lt;/h3&gt;
&lt;p&gt;附帶一提，一個 task 是可以被 &lt;code&gt;enhance&lt;/code&gt; 多次的，每次 &lt;code&gt;enhance&lt;/code&gt; 的結果會依照先後順序疊加：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;task &lt;span style="color:#e6db74"&gt;:before&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; puts &lt;span style="color:#e6db74"&gt;&amp;#39;before task&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;task &lt;span style="color:#e6db74"&gt;:before2&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; puts &lt;span style="color:#e6db74"&gt;&amp;#39;before task 2&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;Rake&lt;/span&gt;&lt;span style="color:#f92672"&gt;::&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;Task&lt;/span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;hello:world&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;].&lt;/span&gt;enhance(&lt;span style="color:#e6db74"&gt;%w[before]&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; puts &lt;span style="color:#e6db74"&gt;&amp;#39;after task&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;Rake&lt;/span&gt;&lt;span style="color:#f92672"&gt;::&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;Task&lt;/span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;hello:world&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;].&lt;/span&gt;enhance(&lt;span style="color:#e6db74"&gt;%w[before2]&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; puts &lt;span style="color:#e6db74"&gt;&amp;#39;after task 2&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ bundle exec rake invoke
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, man.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, Earth~
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;before task
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;before task &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, world!
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;after task
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;after task &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;本文中所有的 code 都放在&lt;a href="https://github.com/dwy6626/rake-experiment/"&gt;這個 repo&lt;/a&gt; 的 &lt;code&gt;hello.rake&lt;/code&gt; 檔案內：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/dwy6626/rake-experiment/blob/master/lib/tasks/hello.rake"&gt;https://github.com/dwy6626/rake-experiment/blob/master/lib/tasks/hello.rake&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="參考資料"&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.rubydoc.info/gems/rake/Rake/Task"&gt;Rake::Task 官方文件&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/molly/rake-task-enhance-method-explained-3bo0"&gt;Rake::Task .enhance() Method Explained&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>如何在 Rails 中寫 Rake Tasks</title><link>https://dwye.dev/post/rake/</link><pubDate>Sun, 27 Jun 2021 13:41:25 +0800</pubDate><guid>https://dwye.dev/post/rake/</guid><description>
&lt;p&gt;本文使用當下最新的 Ruby 3.0.1 和 Rails 6.1.4 作為示範&lt;/p&gt;
&lt;h2 id="什麼是-rake"&gt;什麼是 Rake&lt;/h2&gt;
&lt;p&gt;Rake 就像是 Ruby 的 Make，或是 Golang 的 Mage，其實也就是可以讓我們寫一些基本的 task 和 build 程序的地方。&lt;/p&gt;
&lt;p&gt;在 Ruby on Rails 裡面，已經自帶了幾個常用的 rake tasks（有時候我們也會簡稱 rake）:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;rake about
rake db:create
rake db:migrate
rake db:seed
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;當然還有更多，可以用 &lt;code&gt;rake -T&lt;/code&gt; 來查看。&lt;/p&gt;
&lt;h2 id="rails-的-rakefile"&gt;Rails 的 Rakefile&lt;/h2&gt;
&lt;p&gt;Rakefile 其實就是 Ruby 的 Makefile，是用來定義 rake 的地方。&lt;/p&gt;
&lt;p&gt;不過如果你在 Rails 的框架中打開 Rakefile，會發現只有簡單兩行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;require_relative &lt;span style="color:#e6db74"&gt;&amp;#34;config/application&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;Rails&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;application&lt;span style="color:#f92672"&gt;.&lt;/span&gt;load_tasks
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果你把 Rails 的原始碼打開，發現其中第二行會去讀取 &lt;code&gt;lib/tasks&lt;/code&gt; 裏面的所有檔案:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# https://github.com/rails/rails/blob/v6.1.4/railties/lib/rails/engine.rb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;load_tasks&lt;/span&gt;(app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; require &lt;span style="color:#e6db74"&gt;&amp;#34;rake&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; run_tasks_blocks(app)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;run_tasks_blocks&lt;/span&gt;(&lt;span style="color:#f92672"&gt;*&lt;/span&gt;) &lt;span style="color:#75715e"&gt;#:nodoc:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;super&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; paths&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;lib/tasks&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;].&lt;/span&gt;existent&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sort&lt;span style="color:#f92672"&gt;.&lt;/span&gt;each { &lt;span style="color:#f92672"&gt;|&lt;/span&gt;ext&lt;span style="color:#f92672"&gt;|&lt;/span&gt; load(ext) }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;也就是說我們不只可以在 &lt;code&gt;Rakefile&lt;/code&gt; 寫 task，也可以在 &lt;code&gt;lib/tasks&lt;/code&gt; 寫，更方便大型專案的管理。&lt;/p&gt;
&lt;p&gt;附帶一提，因為 &lt;code&gt;Rails.application&lt;/code&gt; 繼承了上面的 &lt;code&gt;Engine&lt;/code&gt; class，他還會另外去讀取 &lt;a href="https://github.com/rails/rails/tree/v6.1.4/railties/lib/rails/tasks"&gt;Rails 自定義的常用 rake 們&lt;/a&gt;，也就是剛剛提到的 &lt;code&gt;db:create&lt;/code&gt; 之類的 rakes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# https://github.com/rails/rails/blob/v6.1.4/railties/lib/rails/application.rb#L526&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;run_tasks_blocks&lt;/span&gt;(app) &lt;span style="color:#75715e"&gt;#:nodoc:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; railties&lt;span style="color:#f92672"&gt;.&lt;/span&gt;each { &lt;span style="color:#f92672"&gt;|&lt;/span&gt;r&lt;span style="color:#f92672"&gt;|&lt;/span&gt; r&lt;span style="color:#f92672"&gt;.&lt;/span&gt;run_tasks_blocks(app) }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;super&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; load &lt;span style="color:#e6db74"&gt;&amp;#34;rails/tasks.rb&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; task &lt;span style="color:#e6db74"&gt;:environment&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ActiveSupport&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;on_load(&lt;span style="color:#e6db74"&gt;:before_initialize&lt;/span&gt;) { config&lt;span style="color:#f92672"&gt;.&lt;/span&gt;eager_load &lt;span style="color:#f92672"&gt;=&lt;/span&gt; config&lt;span style="color:#f92672"&gt;.&lt;/span&gt;rake_eager_load }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; require_environment!
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;而且在 Rails 中最基本的 &lt;code&gt;environment&lt;/code&gt; rake 也是定義在這裡，他會去讀取 &lt;code&gt;config/environment&lt;/code&gt; 這個檔案。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# https://github.com/rails/rails/blob/v6.1.4/railties/lib/rails/application.rb#L365&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;require_environment!&lt;/span&gt; &lt;span style="color:#75715e"&gt;#:nodoc:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; environment &lt;span style="color:#f92672"&gt;=&lt;/span&gt; paths&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;config/environment&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;].&lt;/span&gt;existent&lt;span style="color:#f92672"&gt;.&lt;/span&gt;first
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; require environment &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; environment
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="rake-怎麼寫"&gt;Rake 怎麼寫&lt;/h2&gt;
&lt;p&gt;實際打開一個 Rails 新專案，會發現 &lt;code&gt;lib/tasks&lt;/code&gt; 這個資料夾已經幫我們保留下來了。在裡面新增一個 rake task:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# lib/tasks/hello.rake&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;namespace &lt;span style="color:#e6db74"&gt;:hello&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; desc &lt;span style="color:#e6db74"&gt;&amp;#39;print hello world&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; task &lt;span style="color:#e6db74"&gt;:world&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; puts &lt;span style="color:#e6db74"&gt;&amp;#39;Hello, world!&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這裡出現了三個 function，或著是說，Rake 的 DSL:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;namespace&lt;/code&gt;: 如其名，可以幫助 rake task 命名與分類管理&lt;/li&gt;
&lt;li&gt;&lt;code&gt;desc&lt;/code&gt;: &lt;code&gt;rake -T&lt;/code&gt; 時顯示的描述，rake 必須加上 &lt;code&gt;desc&lt;/code&gt; 才會顯示在 &lt;code&gt;rake -T&lt;/code&gt; 的列表中&lt;/li&gt;
&lt;li&gt;&lt;code&gt;task&lt;/code&gt;: 定義 task 名稱與內容&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;附上 Rake 的文件，裡面有更完整的用法：&lt;a href="https://ruby.github.io/rake/Rake/DSL.html"&gt;https://ruby.github.io/rake/Rake/DSL.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;執行剛寫好的 rake task:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ bundle exec rake hello:world
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, world!
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="task-dependencies"&gt;Task Dependencies&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;task&lt;/code&gt; 這個 DSL 可以吃除了名稱之外的參數：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;task task_name
task task_name: dependencies
task task_name, arguments → dependencies
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;剛剛我們用的是第一個定義。&lt;br&gt;
接下來介紹 dependencies，其實就是在執行這個 rake task 時，哪些 rake 需要被確保執行。&lt;/p&gt;
&lt;p&gt;我們舉最常用的 database 操作相關的 rake 為例子（只截取部分片段）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# https://github.com/rails/rails/blob/v6.1.4/activerecord/lib/active_record/railties/databases.rake&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;db_namespace &lt;span style="color:#f92672"&gt;=&lt;/span&gt; namespace &lt;span style="color:#e6db74"&gt;:db&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; desc &lt;span style="color:#e6db74"&gt;&amp;#34;Creates the database, loads the schema, and initializes with the seed data (use db:reset to also drop the database first)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; task &lt;span style="color:#e6db74"&gt;setup&lt;/span&gt;: &lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;db:create&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;:environment&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;db:schema:load&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;:seed&lt;/span&gt;&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這是 &lt;code&gt;db:setup&lt;/code&gt; 這個 rake task 的定義，傳給 task 的是個 hash，一個 key value pair，括號在這裡省略了，&lt;code&gt;setup&lt;/code&gt; 是 &lt;code&gt;task_name&lt;/code&gt;，而後面的 array 則是 &lt;code&gt;dependencies&lt;/code&gt;。&lt;br&gt;
這段 code 的意思是說，當我執行 &lt;code&gt;db:setup&lt;/code&gt; 時，會需要&lt;strong&gt;先&lt;/strong&gt;跑 dependencies 裡面的 task。&lt;br&gt;
這裡的「先」指的是和 task 後面接的 block 相比較，&lt;strong&gt;會先跑 dependencies 才跑 block 內的東西&lt;/strong&gt;。不巧 &lt;code&gt;db:setup&lt;/code&gt; 剛好沒有 block，所以就是把 &lt;code&gt;db:create&lt;/code&gt;、&lt;code&gt;environment&lt;/code&gt;、&lt;code&gt;db:schema:load&lt;/code&gt;、&lt;code&gt;seed&lt;/code&gt; 這幾個 rake 依序執行。&lt;/p&gt;
&lt;p&gt;我們可以修改剛剛的 &lt;code&gt;hello:world&lt;/code&gt; task 來測試 block 和 dependencies 的順序：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# lib/tasks/hello.rake&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;namespace &lt;span style="color:#e6db74"&gt;:hello&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; desc &lt;span style="color:#e6db74"&gt;&amp;#39;print hello world&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; task &lt;span style="color:#e6db74"&gt;world&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;%w[hello:man]&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; puts &lt;span style="color:#e6db74"&gt;&amp;#39;Hello, world!&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; task &lt;span style="color:#e6db74"&gt;:man&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; puts &lt;span style="color:#e6db74"&gt;&amp;#39;Hello, man.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;再跑一次 &lt;code&gt;hello:world&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ bundle exec rake hello:world
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, man.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, world!
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;會發現寫在 dependencies 的 &lt;code&gt;hello:man&lt;/code&gt; 的確會先被執行，接著才執行 block 內的東西。&lt;/p&gt;
&lt;p&gt;那既然叫做 dependencies，如果被呼叫很多遍，會發生什麼事呢？&lt;br&gt;
可以在同個 namespace 內再定義一個 rake 來實驗看看：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# lib/tasks/hello.rake&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;namespace &lt;span style="color:#e6db74"&gt;:hello&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; desc &lt;span style="color:#e6db74"&gt;&amp;#39;print hello world&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; task &lt;span style="color:#e6db74"&gt;world&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;%w[hello:man hello:earth]&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; puts &lt;span style="color:#e6db74"&gt;&amp;#39;Hello, world!&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; task &lt;span style="color:#e6db74"&gt;earth&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;%w[hello:man]&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; puts &lt;span style="color:#e6db74"&gt;&amp;#39;Hello, Earth~&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; task &lt;span style="color:#e6db74"&gt;:man&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; puts &lt;span style="color:#e6db74"&gt;&amp;#39;Hello, man.&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;現在 &lt;code&gt;world&lt;/code&gt; depends on &lt;code&gt;man&lt;/code&gt; 和 &lt;code&gt;earth&lt;/code&gt;，而 &lt;code&gt;earth&lt;/code&gt; 也 depends on &lt;code&gt;man&lt;/code&gt;。根據我們對 dependency （相依）這個詞的了解，&lt;code&gt;hello:man&lt;/code&gt; 應該只會被執行一遍。&lt;br&gt;
而事實正是如此：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ bundle exec rake hello:world
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, man.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, Earth~
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, world!
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以使用 &lt;code&gt;--trace&lt;/code&gt; 來追蹤 rake task 彼此之間被 invoke 和執行的過程：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ bundle exec rake hello:world --trace
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;** Invoke hello:world &lt;span style="color:#f92672"&gt;(&lt;/span&gt;first_time&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;** Invoke hello:man &lt;span style="color:#f92672"&gt;(&lt;/span&gt;first_time&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;** Execute hello:man
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, man.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;** Invoke hello:earth &lt;span style="color:#f92672"&gt;(&lt;/span&gt;first_time&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;** Invoke hello:man
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;** Execute hello:earth
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, Earth~
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;** Execute hello:world
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello, world!
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>一次搞懂物件導向程式設計的特性</title><link>https://dwye.dev/post/oop-design-principles/</link><pubDate>Mon, 31 May 2021 00:41:25 +0800</pubDate><guid>https://dwye.dev/post/oop-design-principles/</guid><description>
&lt;p&gt;會寫程式很簡單，寫出好的程式很難。上次寫了一篇 &lt;a href="https://dwye.dev/post/clean-code/"&gt;Clean Code 筆記&lt;/a&gt;，算是比較進階的，最近剛好複習了比較基本的一些物件導向相關原則與特性，於是就寫出了這篇。&lt;/p&gt;
&lt;p&gt;這些物件導向的基本觀念和 Clean Code 一樣，平常可以幫助自己讓 code 寫得更易懂且更好維護，而且 Code Review 時可以拿來嗆同事（Ｘ），同時也算是面試的熱門考題之一，因此一次整理成一篇記下來，希望能幫助到自己和其他剛好找到這篇的人。&lt;/p&gt;
&lt;p&gt;這邊主要以條列和簡單說明為主，因為我相信原則和定律皆由其簡單的解釋方式，若要更深的討論也是可以分別寫成文章的，但就不是這篇的目的了。&lt;/p&gt;
&lt;p&gt;如果你認為有寫錯或需要補充的，也歡迎留言或&lt;a href="https://dwye.dev/about/"&gt;寄信&lt;/a&gt;告訴我。&lt;/p&gt;
&lt;h2 id="software-implementation-goal"&gt;Software Implementation Goal&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Robustness 可以處理預料外的 input&lt;/li&gt;
&lt;li&gt;Adaptability 在不同平台與環境之間的變動越小越好（現在可以透過 docker 實現）&lt;/li&gt;
&lt;li&gt;Reusability 重複使用&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="design-pattern-的面向"&gt;Design Pattern 的面向&lt;/h3&gt;
&lt;p&gt;Algorithms: Recursion / Divide-and-conquer / Amortization &amp;hellip;&lt;/p&gt;
&lt;p&gt;Software Design: Template method / Composition / Adapter / Decorator &amp;hellip;&lt;/p&gt;
&lt;p&gt;Software Architecture: MVC / Event Sourcing &amp;hellip;&lt;/p&gt;
&lt;h2 id="物件導向程式設計object-oriented-programmingoop"&gt;物件導向程式設計（Object-Oriented Programming，OOP）&lt;/h2&gt;
&lt;h3 id="三個-design-principle"&gt;三個 Design Principle&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Abstraction 抽象化
&lt;ul&gt;
&lt;li&gt;將一個複雜的系統，用簡單的方式精準呈現，而不需要知道其實作細節
&lt;ul&gt;
&lt;li&gt;ADT abstract data type: 描述資料結構&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;可藉由定義 Public interface 來實現&lt;/li&gt;
&lt;li&gt;Interface vs Class: Interface 是抽象的（what），Class 是細節的（how）
&lt;ul&gt;
&lt;li&gt;A class implements an interface&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Encapsulation 封裝
&lt;ul&gt;
&lt;li&gt;一個元件的內部狀態要可以被隱藏&lt;/li&gt;
&lt;li&gt;Public / Protected / Private
&lt;ul&gt;
&lt;li&gt;Protected 的子類別可以存取，而 Private 不行&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Modularity 模組化
&lt;ul&gt;
&lt;li&gt;軟體的不同元件要被分散於不同 functional unit&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="hierarchical-organization"&gt;Hierarchical Organization&lt;/h2&gt;
&lt;p&gt;→ reusability&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Inheritance 繼承
&lt;ul&gt;
&lt;li&gt;Specialization: 於子類別 override 其 methods&lt;/li&gt;
&lt;li&gt;Extension: 於子類別新增 methods&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Polymorphism 多型
&lt;ul&gt;
&lt;li&gt;一個變數可以是多種型別&lt;/li&gt;
&lt;li&gt;同名 function 能根據其 Input 型別 去 call 不同 function → Overloading&lt;/li&gt;
&lt;li&gt;同名 method 能根據其 Class 去 call 實際上於不同 class 的 method → Duck Typing&lt;/li&gt;
&lt;li&gt;類似 functional programming 的 pattern matching&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;網路上常看到的三大特性：封裝、繼承、多型&lt;/p&gt;
&lt;h2 id="solid-principle"&gt;SOLID Principle&lt;/h2&gt;
&lt;p&gt;目的：減少「改程式」。&lt;/p&gt;
&lt;h3 id="single-responsibility-principle--srp-單一職責原則"&gt;Single Responsibility Principle / SRP 單一職責原則&lt;/h3&gt;
&lt;p&gt;一個 function 應該只有一個讓你去修改他的理由（一種職責）&lt;/p&gt;
&lt;h3 id="open-closed-principle--ocp-開放閉合原則"&gt;Open Closed Principle / OCP 開放閉合原則&lt;/h3&gt;
&lt;p&gt;要設計得易於延展（例如：繼承、增加新 function），不用因新型態、新需求加入而改變原本的部分&lt;/p&gt;
&lt;h3 id="liskov-substitution-principle--lsp-里氏替換原則"&gt;Liskov Substitution Principle / LSP 里氏替換原則&lt;/h3&gt;
&lt;p&gt;子類別要能夠完全支援父類別的功能&lt;/p&gt;
&lt;h3 id="interface-segregation-principles--isp-介面隔離原則"&gt;Interface Segregation Principles / ISP 介面隔離原則&lt;/h3&gt;
&lt;p&gt;介面功能單一化，不要有龐大的介面&lt;/p&gt;
&lt;h3 id="dependency-inversion-principle--dip-依賴反轉原則"&gt;Dependency Inversion Principle / DIP 依賴反轉原則&lt;/h3&gt;
&lt;p&gt;Dependency Injection 依賴注入：讓實作類別 (class) 依賴抽象類別 (interface)，不要讓兩個實作類別直接依賴&lt;/p&gt;
&lt;h2 id="參考資料"&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Data Structures and Algorithms in C++ 2/e by Michael T. Goodrich, Roberto Tamassia, David M. Mount&lt;/li&gt;
&lt;li&gt;&lt;a href="http://teddy-chen-tw.blogspot.com/2014/04/solid.html"&gt;SOLID：五則皆變 by 搞笑談軟工&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zh.wikipedia.org/wiki/SOLID_%28%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E8%AE%BE%E8%AE%A1%29"&gt;維基百科頁面&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>Python 費氏數列解法（五）：不同解法的執行時間比較</title><link>https://dwye.dev/post/python-fibonacci-5/</link><pubDate>Sat, 29 May 2021 20:18:25 +0800</pubDate><guid>https://dwye.dev/post/python-fibonacci-5/</guid><description>
&lt;p&gt;終於來到最後一篇了，前面幾篇依序寫了各種不同費氏數列的寫法，包含：&lt;a href="https://dwye.dev/post/python-fibonacci/"&gt;遞迴、迭代&lt;/a&gt;、&lt;a href="https://dwye.dev/post/python-fibonacci-2/"&gt;矩陣&lt;/a&gt;、從矩陣衍生的&lt;a href="https://dwye.dev/post/python-fibonacci-3/"&gt;Fast doubling&lt;/a&gt;、以及最後&lt;a href="https://dwye.dev/post/python-fibonacci-4/"&gt;公式解&lt;/a&gt;和補充的&lt;a href="https://dwye.dev/post/python-decimal/"&gt;精確版公式解&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;從時間複雜度來看，從矩陣開始都是 $O(\lg n)$（如果我們把公式解內指數運算當作 $O(\lg n)$ 的話），那這三個哪個比較快呢？&lt;/p&gt;
&lt;p&gt;&lt;a href="https://yodalee.me/2019/02/2019_fibonacci/"&gt;這篇&lt;/a&gt;文章裡面用 c 比較了 fast doubling 和公式解，發現號稱常數時間複雜度的公式解，反而慢上很多，而且還需要考慮精確度問題。&lt;/p&gt;
&lt;p&gt;所以，Fast doubling 勝。&lt;/p&gt;
&lt;p&gt;但在 Python 又是如何呢？前幾篇我們都在講 Python，因此所以我寫了一段 Python code 來做實驗，把這幾篇提到的方法全部一起比較了一下。&lt;/p&gt;
&lt;h2 id="實驗結果"&gt;實驗結果&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://i.imgur.com/emlCKFs.png" alt="fibonacci time comparison"&gt;&lt;/p&gt;
&lt;p&gt;對照如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;fib_binet: 公式解（Decimal 實作版本，&lt;a href="https://dwye.dev/post/python-decimal/"&gt;補充篇&lt;/a&gt;），時間複雜度 $O(\lg n)$&lt;/li&gt;
&lt;li&gt;fib_fast_double: 遞迴版本的 fast double（&lt;a href="%28/post/python-fibonacci-3%29"&gt;第三篇&lt;/a&gt;），時間複雜度 $O(\lg n)$&lt;/li&gt;
&lt;li&gt;fib_fast_double_iter: 迭代版本的 fast double（&lt;a href="%28/post/python-fibonacci-3%29"&gt;第三篇&lt;/a&gt;），時間複雜度 $O(\lg n)$，最快的一個&lt;/li&gt;
&lt;li&gt;fib_matrix: 矩陣解法（&lt;a href="%28/post/python-fibonacci-2%29"&gt;第二篇&lt;/a&gt;），時間複雜度 $O(\lg n)$&lt;/li&gt;
&lt;li&gt;fib_iterative: 迭代解法（&lt;a href="%28/post/python-fibonacci-1%29"&gt;第一篇&lt;/a&gt;），時間複雜度 $O(n)$&lt;/li&gt;
&lt;li&gt;fib_lru: 遞迴解法，加上 lru_cache（&lt;a href="%28/post/python-fibonacci-1%29"&gt;第一篇&lt;/a&gt;），時間複雜度 $O(n)$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;看看那個公式解浮在那個地方 XDDD&lt;/p&gt;
&lt;p&gt;可能是 Decimal 把精確度開高之後效能犧牲太多了，因為常數部分被拉了很高，曲線看起來很接近 $O(1)$。&lt;/p&gt;
&lt;p&gt;如果刪掉公式解之後，其他解法的結果如下：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://i.imgur.com/6WCIyFC.png" alt="fibonacci time comparison, binet removed"&gt;&lt;/p&gt;
&lt;p&gt;相較其他每個解法都跑得快多了，即便是 $O(n)$ 的基本遞迴和迭代也是，有趣的是，使用 lru_cache 的遞迴速度仍然比相同複雜度的迭代版本要來得慢不少，我猜也許是因為重複 call function 並處理 cache 所造成的的 overhead 在 Python 內還是不小，所以能不要偷懶還是不要偷懶，就改寫成 iterative 吧。&lt;/p&gt;
&lt;p&gt;矩陣解和 fast double 雖然都是 $O(lg n)$，但兩者常數也是插上很多，雖然增長速度都很慢，但 fast_double 快上一截，而且又以迭代版本的比遞迴版本的更快。&lt;/p&gt;
&lt;p&gt;最後附上實驗的原始碼：&lt;/p&gt;
&lt;script src="https://gist.github.com/dwy6626/ca70c57da3b79daae1bd8df05e98d0c2.js"&gt;&lt;/script&gt;
&lt;p&gt;測試 CPU 是 4.00GHz 的 i7-6700K，作業系統是 ubuntu 18.04，Python 3.9.1。&lt;/p&gt;
&lt;h2 id="後記"&gt;後記&lt;/h2&gt;
&lt;p&gt;拖稿了很久，終於來完成這系列文章了。&lt;/p&gt;
&lt;p&gt;原先只打算寫個兩篇，沒想到越深入研究就越拆越多篇出來。其實費氏數列相關的討論還有很多，也有負數、無理數甚至複數的推廣，但後面就是數學系的事情了，對於工數只有線性代數和一點點微分方程基礎的我還是先在此打住好了 XD&lt;/p&gt;
&lt;p&gt;可以來寫最近學的其他東西了～一坑開完又是一坑，這個系列實在有點解題+興趣導向，下次來寫點更實用的好了。&lt;/p&gt;
&lt;h2 id="參考資料"&gt;參考資料：&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hackmd.io/@sysprog/fibonacci-number"&gt;費氏數列分析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yodalee.me/2019/02/2019_fibonacci/"&gt;關於費式數列的那些事&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Generalizations_of_Fibonacci_numbers"&gt;Generalizations of Fibonacci numbers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>使用 Decimal 提高浮點運算精確度</title><link>https://dwye.dev/post/python-decimal/</link><pubDate>Fri, 28 May 2021 20:18:25 +0800</pubDate><guid>https://dwye.dev/post/python-decimal/</guid><description>
&lt;p&gt;在&lt;a href="https://dwye.dev/post/python-fibonacci-4/"&gt;第四篇&lt;/a&gt;提到費氏數列的公式解會遇到浮點數問題，只能精準算到 122 位。因為浮點數是利用二進位的小數來做儲存，對於非二進位的數字會有誤差。而且其儲存位數有上限，對於無理數的運算，超過一定的大小就會出現精確度問題。&lt;/p&gt;
&lt;p&gt;所以這篇就要來簡單使用 Python 內建的 Decimal module 來拉高浮點數運算的精確度。&lt;/p&gt;
&lt;h2 id="以費氏數列公式解為例"&gt;以費氏數列公式解為例&lt;/h2&gt;
&lt;p&gt;首先回顧一下公式：&lt;/p&gt;
&lt;p&gt;$$&lt;br&gt;
F_n = \frac{1}{\sqrt{5}}\left( (\frac{1 + \sqrt{5}}{2})^n - (\frac{1 - \sqrt{5}}{2})^n \right)&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;可以注意到裡面有無理數，也就是黃金比例的部分。對無理數做 n 次方運算，當 n 放大後很明顯誤差也會樂乘越大。為了提高精確度，我們可以提高儲存的位元。&lt;/p&gt;
&lt;h3 id="decimal-module"&gt;Decimal module&lt;/h3&gt;
&lt;p&gt;Python 提供了一個內建 module：&lt;code&gt;Decimal&lt;/code&gt;，是一個十進位運算模型，可以避免一些浮點數誤差問題：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;In [&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;]: &lt;span style="color:#ae81ff"&gt;0.1&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0.3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Out[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;]: &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;In [&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;]: &lt;span style="color:#f92672"&gt;from&lt;/span&gt; decimal &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Decimal
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;...&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;In [&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;]: Decimal(&lt;span style="color:#e6db74"&gt;&amp;#39;0.1&amp;#39;&lt;/span&gt;) &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; &lt;span style="color:#f92672"&gt;==&lt;/span&gt; Decimal(&lt;span style="color:#e6db74"&gt;&amp;#39;0.3&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Out[&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;]: &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意我們是使用 &lt;code&gt;Decimal('0.1')&lt;/code&gt; 而不是 &lt;code&gt;Decimal(0.1)&lt;/code&gt;，因為後者會把不精確的 float 0.1 丟到 Decimal，還是變得不精確：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;In [&lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;]: Decimal(&lt;span style="color:#ae81ff"&gt;0.1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Out[&lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;]: Decimal(&lt;span style="color:#e6db74"&gt;&amp;#39;0.1000000000000000055511151231257827021181583404541015625&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="套用到費氏數列公式解"&gt;套用到費氏數列公式解&lt;/h3&gt;
&lt;p&gt;原本的 code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fib_binet&lt;/span&gt;(n):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; golden_ratio &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt; &lt;span style="color:#f92672"&gt;**&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;) &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; golden_ratio_alt &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt; &lt;span style="color:#f92672"&gt;**&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;) &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; round((golden_ratio &lt;span style="color:#f92672"&gt;**&lt;/span&gt; n &lt;span style="color:#f92672"&gt;-&lt;/span&gt; golden_ratio_alt &lt;span style="color:#f92672"&gt;**&lt;/span&gt; n) &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt; &lt;span style="color:#f92672"&gt;**&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我們把數字用 Decimal 包起來：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;accurate_fib_binet&lt;/span&gt;(n):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sqrt5 &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Decimal(&lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;) &lt;span style="color:#f92672"&gt;**&lt;/span&gt; Decimal(&lt;span style="color:#e6db74"&gt;&amp;#39;0.5&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; golden_ratio &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; sqrt5) &lt;span style="color:#f92672"&gt;/&lt;/span&gt; Decimal(&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; golden_ratio_alt &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; sqrt5) &lt;span style="color:#f92672"&gt;/&lt;/span&gt; Decimal(&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; round((golden_ratio &lt;span style="color:#f92672"&gt;**&lt;/span&gt; Decimal(n) &lt;span style="color:#f92672"&gt;-&lt;/span&gt; golden_ratio_alt &lt;span style="color:#f92672"&gt;**&lt;/span&gt; Decimal(n)) &lt;span style="color:#f92672"&gt;/&lt;/span&gt; sqrt5)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;並拿&lt;a href="https://dwye.dev/post/python-fibonacci-3/"&gt;矩陣解法&lt;/a&gt;的 &lt;code&gt;fib_fast_double_iter&lt;/code&gt; 來比較：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;x &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; x &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; diff &lt;span style="color:#f92672"&gt;=&lt;/span&gt; decimal_fib_binet(x) &lt;span style="color:#f92672"&gt;-&lt;/span&gt; fib_fast_double_iter(x)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; diff &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(x, diff)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ python binet_de.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;123&lt;/span&gt; -1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;嗯？跟原本一樣？&lt;/p&gt;
&lt;p&gt;主要是因為我們的問題最主要是儲存位數不夠，畢竟我們原本的數值就是無理數，用十進位也無法精確表示。&lt;/p&gt;
&lt;p&gt;然而 Decimal module 提供了我們提升精確度的方法：&lt;/p&gt;
&lt;h3 id="以空間換取精確度"&gt;以空間換取精確度&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; decimal &lt;span style="color:#f92672"&gt;import&lt;/span&gt; getcontext
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;getcontext()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;prec &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;10000&lt;/span&gt; &lt;span style="color:#75715e"&gt;# default is 28&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;把上面這段 code 加進去之後，原本的 while loop 就會跑很久&amp;hellip;，也就代表數字到很大的時候，還是可以算準。我自己測試約可以到四萬附近。&lt;/p&gt;
&lt;p&gt;不過精確度越大，就會算越久，可以從 &lt;code&gt;MAX_PREC&lt;/code&gt; 這個常數看最大可以設到多少：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;In [&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;]: &lt;span style="color:#f92672"&gt;from&lt;/span&gt; decimal &lt;span style="color:#f92672"&gt;import&lt;/span&gt; MAX_PREC
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;...&lt;/span&gt;: MAX_PREC
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Out[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;]: &lt;span style="color:#ae81ff"&gt;999999999999999999&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;不要真的設到這麼大，不然會卡住 &amp;hellip;&lt;/p&gt;
&lt;p&gt;但實際上還是要看應用情境吧，對我來說如果不知道進來的數字會多大，不如直接使用公式解以外的算法比較保險。&lt;/p&gt;
&lt;h2 id="reference"&gt;Reference&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.python.org/3/library/decimal.html"&gt;Decimal - Decimal fixed point and floating point arithmetic&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>Python 費氏數列解法（四）：公式解與推導</title><link>https://dwye.dev/post/python-fibonacci-4/</link><pubDate>Sun, 09 May 2021 17:18:25 +0800</pubDate><guid>https://dwye.dev/post/python-fibonacci-4/</guid><description>
&lt;p&gt;在&lt;a href="https://dwye.dev/post/python-fibonacci-2/"&gt;第二篇&lt;/a&gt;介紹了費氏數列的矩陣解法，不過費氏數列其實是可以直接用公式算出第 n 項的值的，這邊就來介紹並推導一下公式解，順便幫自己複習一下數學 XD&lt;/p&gt;
&lt;h2 id="公式解binets-formula"&gt;公式解：Binet&amp;rsquo;s Formula&lt;/h2&gt;
&lt;p&gt;$$&lt;br&gt;
F_n = \frac{1}{\sqrt{5}}\left( (\frac{1 + \sqrt{5}}{2})^n - (\frac{1 - \sqrt{5}}{2})^n \right)&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;是的，$\frac{1 + \sqrt{5}}{2}$，傳說中的黃金比例。&lt;/p&gt;
&lt;p&gt;但奇怪，費氏數列不是都是整數嗎？為什麼會跑出一堆根號呢？不信邪的話自己寫程式去跑，就會發現解剛好都是整數：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# binet.py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fib_binet&lt;/span&gt;(n):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; golden_ratio &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt; &lt;span style="color:#f92672"&gt;**&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;) &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; golden_ratio_alt &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt; &lt;span style="color:#f92672"&gt;**&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;) &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; (golden_ratio &lt;span style="color:#f92672"&gt;**&lt;/span&gt; n &lt;span style="color:#f92672"&gt;-&lt;/span&gt; golden_ratio_alt &lt;span style="color:#f92672"&gt;**&lt;/span&gt; n) &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt; &lt;span style="color:#f92672"&gt;**&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 一般來說，還是會轉換型態成 int:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# return round((golden_ratio ** n - golden_ratio_alt ** n) / 5 ** 0.5)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; __name__ &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; i &lt;span style="color:#f92672"&gt;in&lt;/span&gt; range(&lt;span style="color:#ae81ff"&gt;30&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(fib_binet(i))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ python binet.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;0.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;1.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;1.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;3.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;5.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;8.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;13.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;21.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="公式推導"&gt;公式推導&lt;/h2&gt;
&lt;p&gt;這邊需要一些基本的線性代數知識，主要是對角化的部分。&lt;/p&gt;
&lt;p&gt;對於從量子化學領域跳過來的我，解 eigenvalues 就像回母校一樣充滿了熟悉感 XD&lt;/p&gt;
&lt;p&gt;下面就只附上大概的流程，計算細節就都省略。&lt;/p&gt;
&lt;p&gt;讓我們從&lt;a href="https://dwye.dev/post/python-fibonacci-2/"&gt;第二篇&lt;/a&gt;的矩陣表示法開始：&lt;/p&gt;
&lt;p&gt;$$&lt;br&gt;
\begin{bmatrix}&lt;br&gt;
F(n+1)\\&lt;br&gt;
F(n)&lt;br&gt;
\end{bmatrix} = \begin{bmatrix}&lt;br&gt;
1 &amp;amp; 1\\&lt;br&gt;
1 &amp;amp; 0&lt;br&gt;
\end{bmatrix}^{n} \begin{bmatrix}&lt;br&gt;
1\\&lt;br&gt;
0&lt;br&gt;
\end{bmatrix}&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;中間的矩陣我們令其為 A：&lt;/p&gt;
&lt;p&gt;$$&lt;br&gt;
A = \begin{bmatrix}&lt;br&gt;
1 &amp;amp; 1\\&lt;br&gt;
1 &amp;amp; 0&lt;br&gt;
\end{bmatrix}&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;因為要算 A 的 n 次方，可以將其對角化，對角矩陣的 n 次方即是其每個值的 n 次方：&lt;/p&gt;
&lt;p&gt;$$&lt;br&gt;
A^n = (Q\lambda Q^{-1})^n = Q\lambda^nQ^{-1}&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;而 A 的兩個 eigenvalues 為：&lt;/p&gt;
&lt;p&gt;$$&lt;br&gt;
\frac{1 \pm \sqrt{5}}{2}&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;對應的 eigenvectors 分別為（$+$對$+$，$-$對$-$）：&lt;/p&gt;
&lt;p&gt;$$&lt;br&gt;
t \begin{bmatrix}&lt;br&gt;
1 \\&lt;br&gt;
\frac{- 1 \pm \sqrt{5}}{2}&lt;br&gt;
\end{bmatrix}&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;所以原本的 $A^n$ 就可以利用對角化展開：&lt;/p&gt;
&lt;p&gt;$$&lt;br&gt;
A^n = \begin{bmatrix}&lt;br&gt;
1 &amp;amp; 1\\&lt;br&gt;
1 &amp;amp; 0&lt;br&gt;
\end{bmatrix}^n = \begin{bmatrix}&lt;br&gt;
1 &amp;amp; 1\\&lt;br&gt;
\frac{- 1 + \sqrt{5}}{2} &amp;amp; \frac{- 1 - \sqrt{5}}{2}&lt;br&gt;
\end{bmatrix} \begin{bmatrix}&lt;br&gt;
\frac{1 + \sqrt{5}}{2} &amp;amp; 0\\&lt;br&gt;
0 &amp;amp; \frac{1 - \sqrt{5}}{2}&lt;br&gt;
\end{bmatrix}^n \begin{bmatrix}&lt;br&gt;
1 &amp;amp; 1\\&lt;br&gt;
\frac{- 1 + \sqrt{5}}{2} &amp;amp; \frac{- 1 - \sqrt{5}}{2}&lt;br&gt;
\end{bmatrix}^{-1}&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;帶回原本的式子：&lt;/p&gt;
&lt;p&gt;$$&lt;br&gt;
\begin{bmatrix}&lt;br&gt;
F(n+1)\\&lt;br&gt;
F(n)&lt;br&gt;
\end{bmatrix} = \begin{bmatrix}&lt;br&gt;
1 &amp;amp; 1\\&lt;br&gt;
\frac{- 1 + \sqrt{5}}{2} &amp;amp; \frac{- 1 - \sqrt{5}}{2}&lt;br&gt;
\end{bmatrix} \begin{bmatrix}&lt;br&gt;
\frac{1 + \sqrt{5}}{2} &amp;amp; 0\\&lt;br&gt;
0 &amp;amp; \frac{1 - \sqrt{5}}{2}&lt;br&gt;
\end{bmatrix}^n \begin{bmatrix}&lt;br&gt;
1 &amp;amp; 1\\&lt;br&gt;
\frac{- 1 + \sqrt{5}}{2} &amp;amp; \frac{- 1 - \sqrt{5}}{2}&lt;br&gt;
\end{bmatrix}^{-1} \begin{bmatrix}&lt;br&gt;
1\\&lt;br&gt;
0&lt;br&gt;
\end{bmatrix}&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;剩下就是高中數學的程度了，乘開簡化之後就可以得到公式解了。&lt;/p&gt;
&lt;h2 id="公式解的-issues"&gt;公式解的 issues&lt;/h2&gt;
&lt;h3 id="浮點數精確度"&gt;浮點數精確度&lt;/h3&gt;
&lt;p&gt;可以注意到，公式解多了很多浮點數運算。&lt;/p&gt;
&lt;p&gt;浮點數就是二進位的科學記號，所以當數值大的時候就會有精確度問題出現。因為我們最後會過一個 int，數值小的時候這個誤差其實是不會影響的，但數值夠大就可以看到誤差。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ./binet.py&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;x &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; x &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; diff &lt;span style="color:#f92672"&gt;=&lt;/span&gt; fib_binet(x) &lt;span style="color:#f92672"&gt;-&lt;/span&gt; fib_fast_double_iter(x)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; diff &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(x, diff)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ python binet.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;123&lt;/span&gt; -1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;實際寫個 code 去跑就可以發現，在第 124 項之後就會出現誤差。&lt;/p&gt;
&lt;p&gt;其實用上一篇的&lt;a href="https://dwye.dev/post/python-fibonacci-3/"&gt;fast doubling&lt;/a&gt;來計算，不但沒有慢上多少，也不用擔心誤差問題。&lt;/p&gt;
&lt;h3 id="常數時間"&gt;常數時間？&lt;/h3&gt;
&lt;p&gt;不是程式碼沒有迴圈就是常數時間，這個公式裡面有黃金比例次方：&lt;/p&gt;
&lt;p&gt;$$&lt;br&gt;
(\frac{1 + \sqrt{5}}{2})^n&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;如果是二的次方，可以靠 shift 來達到常數時間運算，但這是個浮點數次方，以前篇提到的&lt;a href="https://dwye.dev/post/python-fibonacci-3/"&gt;快速冪 fast doubling&lt;/a&gt; 來計算也是要 $O(\lg n)$ 的複雜度，即便這個增長很小，在理論上也不是常數時間。&lt;/p&gt;
&lt;h2 id="有趣的小故事"&gt;有趣的小故事&lt;/h2&gt;
&lt;p&gt;其實會來研究 Python 與費氏數列的，與幾年前一串 &lt;a href="https://www.facebook.com/groups/pythontw/permalink/10158445814613438/"&gt;Python Taiwan 的討論串&lt;/a&gt;有關，可惜最熱鬧的那則已經被刪掉了。&lt;/p&gt;
&lt;p&gt;故事大致上就是有人在底下說了費氏數列公式解是 $O(1)$ 的言論，甚至提出只要把 $\pi$ 和 $e$ 等無理數都存在月球上的話，就能直接 $O(1)$ 解決很多問題，嗯&amp;hellip;&lt;/p&gt;
&lt;p&gt;其實 $O(1)$ 在這種時候已經變成迷思了，不然我大可以寫個 function，不管輸入是多少，我都故弄玄虛，算到一個超級大的值，然後再把相對應個輸出丟出去就好，就可以宣稱我是 $O(1)$ 解。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fib_fake_o1&lt;/span&gt;(n):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; max_n &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;300&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; n &lt;span style="color:#f92672"&gt;&amp;gt;=&lt;/span&gt; max_n:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;raise&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Exception&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;n must be &amp;lt; &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;max_n&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; f1, f2 &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; res &lt;span style="color:#f92672"&gt;=&lt;/span&gt; f2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; i &lt;span style="color:#f92672"&gt;in&lt;/span&gt; range(&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;, max_n):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; f1, f2 &lt;span style="color:#f92672"&gt;=&lt;/span&gt; f2, f1 &lt;span style="color:#f92672"&gt;+&lt;/span&gt; f2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; i &lt;span style="color:#f92672"&gt;==&lt;/span&gt; n:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; res &lt;span style="color:#f92672"&gt;=&lt;/span&gt; f2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; res
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Big O 複雜度分析是把常數忽略的，但實務上那個常數也是重要的。&lt;/p&gt;
&lt;h2 id="小結"&gt;小結&lt;/h2&gt;
&lt;p&gt;綜規以上來看，其實公式解也沒這麼厲害，而且也不是真的很省事（誤差問題）。&lt;/p&gt;
&lt;p&gt;不過實務上通常不會需要這麼大的 fibonacci number 吧&amp;hellip;，程式是死的，人是活的，還是要看情況決定哪種解法好。&lt;/p&gt;
&lt;p&gt;大部分時候甚至寫個 O(n) 解法就夠用了。（實際上我面試那天在寫出 O(n）解法之後也沒有再被追問了，畢竟也只是眾多面試題中的其中一個小問題。）&lt;/p&gt;
&lt;h2 id="參考資料"&gt;參考資料：&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.yangjerry.tw/2019/01/31/fibonacci-is-bigO1/"&gt;O (1) 的費氏數列？公式解就一定是 O (1)？ &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dotblogs.com.tw/daniel/2018/11/10/161148"&gt;為什麼 Float 和 Double 會有誤差 (浮點數儲存原理)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>Python 費氏數列解法（三）：Fast Doubling</title><link>https://dwye.dev/post/python-fibonacci-3/</link><pubDate>Tue, 06 Apr 2021 20:18:25 +0800</pubDate><guid>https://dwye.dev/post/python-fibonacci-3/</guid><description>
&lt;p&gt;&lt;a href="https://dwye.dev/post/python-fibonacci-2/"&gt;上篇&lt;/a&gt;寫到費氏數列的矩陣解法來達成 $O(\lg n)$ 的時間複雜度，實際上可以再做一些變化來簡化計算。如果目標時間複雜度是 $O(\lg n)$，代表我們要能每次直接計算當 n 變成兩倍時的數值。&lt;/p&gt;
&lt;p&gt;下面介紹的 Fast Doubling 方法就是這個例子。&lt;/p&gt;
&lt;h2 id="推導"&gt;推導&lt;/h2&gt;
&lt;p&gt;先直接從 2n+1 和 2n 的矩陣開始出發：&lt;/p&gt;
&lt;p&gt;$$&lt;br&gt;
\begin{bmatrix}&lt;br&gt;
F(2n+1)\\&lt;br&gt;
F(2n)&lt;br&gt;
\end{bmatrix} = \begin{bmatrix}&lt;br&gt;
1 &amp;amp; 1\\&lt;br&gt;
1 &amp;amp; 0&lt;br&gt;
\end{bmatrix}^{2n}\begin{bmatrix}&lt;br&gt;
1\\&lt;br&gt;
0&lt;br&gt;
\end{bmatrix}&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;&lt;a href="https://dwye.dev/post/python-fibonacci-2/"&gt;上篇&lt;/a&gt;最後的公式告訴我們，中間 1 1 1 0 的矩陣 n 次方可以替換成費氏數 n 附近的矩陣：&lt;/p&gt;
&lt;p&gt;$$&lt;br&gt;
\begin{bmatrix}&lt;br&gt;
F(2n+1)\\&lt;br&gt;
F(2n)&lt;br&gt;
\end{bmatrix} = \begin{bmatrix}&lt;br&gt;
F(n+1) &amp;amp; F(n)\\&lt;br&gt;
F(n) &amp;amp; F(n-1)&lt;br&gt;
\end{bmatrix}\begin{bmatrix}&lt;br&gt;
F(n+1) &amp;amp; F(n)\\&lt;br&gt;
F(n) &amp;amp; F(n-1)&lt;br&gt;
\end{bmatrix}\begin{bmatrix}&lt;br&gt;
1\\&lt;br&gt;
0&lt;br&gt;
\end{bmatrix}\\&lt;br&gt;
= \begin{bmatrix}&lt;br&gt;
F(n+1)^2 + F(n)^2\\&lt;br&gt;
F(n)(F(n+1) + F(n-1))&lt;br&gt;
\end{bmatrix}\\&lt;br&gt;
= \begin{bmatrix}&lt;br&gt;
F(n+1)^2 + F(n)^2\\&lt;br&gt;
F(n)(2F(n+1) - F(n))&lt;br&gt;
\end{bmatrix}&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;最後一次運算我們把 $F(n-1)$ 用費氏數列的定義替換掉了，即 $F(n-1) = F(n+1) - F(n)$。&lt;/p&gt;
&lt;p&gt;整理一下結果，對於奇數和偶數的不同算法如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$F(2n+1) = F(n+1)^2 + F(n)^2$&lt;/li&gt;
&lt;li&gt;$F(2n) = F(n)(2F(n+1) - F(n))$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此我們每次都可以把 n 變成兩倍，需要進行遞迴次數和&lt;a href="https://dwye.dev/post/python-fibonacci/"&gt;第一篇&lt;/a&gt;的遞迴解法相比，從 $O(n)$ 變成 $O(\lg n)$，所以時間複雜度也跟著變成 $O(\lg n)$。&lt;/p&gt;
&lt;h2 id="實作"&gt;實作&lt;/h2&gt;
&lt;h3 id="遞迴解法"&gt;遞迴解法&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fibonacci&lt;/span&gt;(n):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; recursive(n)[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;recursive&lt;/span&gt;(n):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# calculate f(n+1) and f(n)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; n &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; q, r &lt;span style="color:#f92672"&gt;=&lt;/span&gt; divmod(n, &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; f2, f1 &lt;span style="color:#f92672"&gt;=&lt;/span&gt; recursive(q) &lt;span style="color:#75715e"&gt;# f2 is the larger one&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; f2, f1 &lt;span style="color:#f92672"&gt;=&lt;/span&gt; f1 &lt;span style="color:#f92672"&gt;**&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; f2 &lt;span style="color:#f92672"&gt;**&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;, f1 &lt;span style="color:#f92672"&gt;*&lt;/span&gt; (&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt; f2 &lt;span style="color:#f92672"&gt;-&lt;/span&gt; f1)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; r &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; f2, f1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; f1 &lt;span style="color:#f92672"&gt;+&lt;/span&gt; f2, f2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可能有個疑問是，這個遞迴解法會不會像&lt;a href="https://dwye.dev/post/python-fibonacci/"&gt;第一篇&lt;/a&gt;的遞迴那樣，做了許多重複計算？&lt;br&gt;
答案是&lt;strong&gt;不會&lt;/strong&gt;，因為每次進入 &lt;code&gt;recursive&lt;/code&gt; 的數字一定是前一個的一半，而且 recursive tree 不會有增長，每次呼叫只會重複呼叫自己一次而已，因此不需要另外再做 cache 了。&lt;/p&gt;
&lt;h3 id="迭代解法"&gt;迭代解法&lt;/h3&gt;
&lt;p&gt;迭代解法的思考方向，就是去分析我們在遞迴中每步做了什麼，並用迴圈代替。&lt;/p&gt;
&lt;p&gt;這邊比較麻煩的是，遞迴中有個邏輯判斷，當 &lt;code&gt;recursive&lt;/code&gt; 接受到的輸入是奇數時，必須多一個額外步驟，這樣結果才是正確的（因為奇數和偶數的算法不同，而我們預設的是偶數的計算公式）。&lt;/p&gt;
&lt;p&gt;所以我們將 n 往上增長時，會不確定下一步要算到的是 2n 還是 2n+1，不知道需不需要額外的步驟。&lt;/p&gt;
&lt;p&gt;簡單舉個例子，如果我們要計算的 n 是 21，則遞迴計算的是 [21, 10, 5, 2, 1]。用迭代做 bottom-up 的話，就會是 [1, 2, 5, 10, 21]。我們在計算到 2 的下一項時，沒辦法直接預測下一個要算的是 2n 的 4，還是 2n+1 的 5。&lt;/p&gt;
&lt;p&gt;如果沒辦法預測，就記起來就好了 XD&lt;/p&gt;
&lt;p&gt;所以解法就是建立一個 tracker，先記錄我們需要計算的值，然後 bottom-up 組回來。這邊的 tracker 其實就是個 stack，符合 LIFO 的特性（Last-in, First-out）。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fib_fast_double_iter&lt;/span&gt;(n):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# stack construction&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tracker &lt;span style="color:#f92672"&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; n &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tracker&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(n)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; n &lt;span style="color:#f92672"&gt;//=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# initialization&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; f1, f2 &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# bottom-up&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; tracker:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; n &lt;span style="color:#f92672"&gt;=&lt;/span&gt; tracker&lt;span style="color:#f92672"&gt;.&lt;/span&gt;pop()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; f1, f2 &lt;span style="color:#f92672"&gt;=&lt;/span&gt; f1 &lt;span style="color:#f92672"&gt;*&lt;/span&gt; (&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt; f2 &lt;span style="color:#f92672"&gt;-&lt;/span&gt; f1), f1 &lt;span style="color:#f92672"&gt;**&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; f2 &lt;span style="color:#f92672"&gt;**&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; n &lt;span style="color:#f92672"&gt;%&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; f1, f2 &lt;span style="color:#f92672"&gt;=&lt;/span&gt; f2, f1 &lt;span style="color:#f92672"&gt;+&lt;/span&gt; f2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; f1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href="https://chunminchang.github.io/blog/post/calculating-fibonacci-numbers-by-fast-doubling"&gt;這篇文章&lt;/a&gt;還用了更進階的技巧，利用二進位表示法其實就是不斷除以二的餘數的特性，把 stack 也省下來了，只需要 $O(1)$ 的空間複雜度。這邊就不繼續探討，有興趣的可以自行去閱讀。&lt;/p&gt;
&lt;h2 id="參考資料"&gt;參考資料：&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.geeksforgeeks.org/fast-doubling-method-to-find-the-nth-fibonacci-number/"&gt;Fast Doubling method to find the Nth Fibonacci number&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nayuki.io/page/fast-fibonacci-algorithms"&gt;Fast Fibonacci algorithms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chunminchang.github.io/blog/post/calculating-fibonacci-numbers-by-fast-doubling"&gt;Calculating Fibonacci Numbers by Fast Doubling&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>Hugo 排程發表文章</title><link>https://dwye.dev/post/hugo-scheduled-post/</link><pubDate>Sat, 03 Apr 2021 02:59:59 +0800</pubDate><guid>https://dwye.dev/post/hugo-scheduled-post/</guid><description>
&lt;p&gt;不管是 FB 粉專，或是以前無名小站時代的部落格系統，都有提供排程發表文章的功能，讓寫手在靈感特別多的時候，或是行銷人員希望配合特定時程，可以預先寫好未來要發表的文章，並且在時間到的時候自動發表。&lt;/p&gt;
&lt;p&gt;問題來了，如果是使用像 Hugo 這種靜態網站產生器，能否實現相同的功能呢？&lt;/p&gt;
&lt;p&gt;答案是可以的，&lt;a href="//gohugo.io/troubleshooting/faq/#how-do-i-schedule-posts"&gt;官方網站&lt;/a&gt;有提到：&lt;/p&gt;
&lt;blockquote&gt;
&lt;h3 id="how-do-i-schedule-posts"&gt;How do I schedule posts?&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Set publishDate in the page Front Matter to a date in the future.&lt;/li&gt;
&lt;li&gt;Build and publish at intervals.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;就是叫你把日期設定在未來，然後每隔一段時間部署就好。&lt;/p&gt;
&lt;p&gt;這篇就是要來講我如何使用 GitHub Action 實現排程發表。&lt;/p&gt;
&lt;h2 id="正文"&gt;正文&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://dwye.dev/post/hugo-github-action/"&gt;之前的文章&lt;/a&gt;有提到我把 blog CD 轉換到 GitHub Action。&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.github.com/en/actions/reference/events-that-trigger-workflows"&gt;GitHub Action 的官方文件&lt;/a&gt;提到，可以使用 cronjob 格式來設定排程：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# .github/workflows/deploy.yml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;on&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;push&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;branches&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;master&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;schedule&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;cron&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;0 19 * * *&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 下略&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這邊的時間是 UTC，會在每天 19:00 UTC 跑一次部署，也就是 (19 + 8) % 24 = 台灣時間半夜三點跑 deploy job，重新部署我的 blog。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;：&lt;em&gt;誰會想在凌晨三點部署部落格&lt;/em&gt;&lt;br&gt;
GitHub Action：&lt;em&gt;歐好棒，三點了&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/3am.jpg" alt="歐好棒，三點了"&gt;&lt;/p&gt;
&lt;p&gt;我把兩個條件並列，就會變成 or 關係，任一個條件成立（push on master branch 或是每天的半夜三點），都會 trigger 這個 workflow，有了 CD 是不是很方便呢？&lt;/p&gt;
&lt;p&gt;一個需要注意的是，如果你設定的 interval 太短，會自動被拉長到每五分鐘執行一次。&lt;/p&gt;
&lt;h2 id="預覽未來文章"&gt;預覽未來文章&lt;/h2&gt;
&lt;p&gt;因為 Hugo 預設會忽略未來與草稿文章，可以分別用 &lt;code&gt;-F&lt;/code&gt; 和 &lt;code&gt;-D&lt;/code&gt; 兩個 flag 來讓 Hugo 把這些文章也 build 起來：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ hugo serve -FD
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;想要知道有哪些未來文章可以使用 &lt;code&gt;hugo list&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ hugo list future
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;同理，列出草稿：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ hugo list drafts
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>Python 費氏數列解法（二）：矩陣解</title><link>https://dwye.dev/post/python-fibonacci-2/</link><pubDate>Wed, 31 Mar 2021 20:18:25 +0800</pubDate><guid>https://dwye.dev/post/python-fibonacci-2/</guid><description>
&lt;p&gt;在&lt;a href="https://dwye.dev/post/python-fibonacci/"&gt;上篇&lt;/a&gt;我們討論了費氏數列的各種基本解法。&lt;/p&gt;
&lt;p&gt;原本我也以為 O(n) 的迭代解就已經是標準解法了，直到被大神朋友指正：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;問費氏數列應該是想聽 $O(\lg n)$ 解法吧？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;查了一下還真的有，&lt;a href="https://medium.com/fcamels-notes/%E8%B2%BB%E6%B0%8F%E6%95%B8%E5%88%97-o-logn-%E7%9A%84%E8%A7%A3%E6%B3%95-e36067e57baa"&gt;這篇文章&lt;/a&gt;寫得蠻完整的，這篇會參考該篇文章來撰寫，但會用我自己的話以及 Python 寫出來。&lt;/p&gt;
&lt;h2 id="哪來的矩陣"&gt;哪來的矩陣？&lt;/h2&gt;
&lt;p&gt;$$&lt;br&gt;
\begin{bmatrix}&lt;br&gt;
F(n)\\&lt;br&gt;
F(n-1)&lt;br&gt;
\end{bmatrix} = \begin{bmatrix}&lt;br&gt;
F(n-1) + F(n-2) \\&lt;br&gt;
F(n-1)&lt;br&gt;
\end{bmatrix} = \begin{bmatrix}&lt;br&gt;
1 &amp;amp; 1\\&lt;br&gt;
1 &amp;amp; 0&lt;br&gt;
\end{bmatrix} \begin{bmatrix}&lt;br&gt;
F(n-1)\\&lt;br&gt;
F(n-2)&lt;br&gt;
\end{bmatrix}&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;其實就是將&lt;a href="https://dwye.dev/post/python-fibonacci/"&gt;上篇&lt;/a&gt;的尾遞迴方法及迭代法中每次的運算內容，使用矩陣乘法表達出來。&lt;/p&gt;
&lt;h2 id="有了矩陣可以幹嘛公式推導"&gt;有了矩陣可以幹嘛（公式推導）&lt;/h2&gt;
&lt;p&gt;有了矩陣表示法，對於費氏數列第 n 項 $F(n)$ 就可以表示成：&lt;/p&gt;
&lt;p&gt;$$&lt;br&gt;
\begin{bmatrix}&lt;br&gt;
F(n)\\&lt;br&gt;
F(n-1)&lt;br&gt;
\end{bmatrix} = \begin{bmatrix}&lt;br&gt;
1 &amp;amp; 1\\&lt;br&gt;
1 &amp;amp; 0&lt;br&gt;
\end{bmatrix} &amp;hellip; \begin{bmatrix}&lt;br&gt;
1 &amp;amp; 1\\&lt;br&gt;
1 &amp;amp; 0&lt;br&gt;
\end{bmatrix}\begin{bmatrix}&lt;br&gt;
F(2)\\&lt;br&gt;
F(1)&lt;br&gt;
\end{bmatrix} = \begin{bmatrix}&lt;br&gt;
1 &amp;amp; 1\\&lt;br&gt;
1 &amp;amp; 0&lt;br&gt;
\end{bmatrix}^{n-2} \begin{bmatrix}&lt;br&gt;
1\\&lt;br&gt;
1&lt;br&gt;
\end{bmatrix}&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;兩邊同乘中間那個 1 1 1 0 的矩陣：&lt;/p&gt;
&lt;p&gt;$$&lt;br&gt;
\begin{bmatrix}&lt;br&gt;
1 &amp;amp; 1\\&lt;br&gt;
1 &amp;amp; 0&lt;br&gt;
\end{bmatrix}&lt;br&gt;
\begin{bmatrix}&lt;br&gt;
F(n)\\&lt;br&gt;
F(n-1)&lt;br&gt;
\end{bmatrix} = \begin{bmatrix}&lt;br&gt;
F(n+1)\\&lt;br&gt;
F(n)&lt;br&gt;
\end{bmatrix} = \begin{bmatrix}&lt;br&gt;
1 &amp;amp; 1\\&lt;br&gt;
1 &amp;amp; 0&lt;br&gt;
\end{bmatrix}^{n-1} \begin{bmatrix}&lt;br&gt;
1\\&lt;br&gt;
1&lt;br&gt;
\end{bmatrix}&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;又：&lt;/p&gt;
&lt;p&gt;$$&lt;br&gt;
\begin{bmatrix}&lt;br&gt;
F(n)\\&lt;br&gt;
F(n-1)&lt;br&gt;
\end{bmatrix} = \begin{bmatrix}&lt;br&gt;
1 &amp;amp; 1\\&lt;br&gt;
1 &amp;amp; 0&lt;br&gt;
\end{bmatrix}^{n-1} \begin{bmatrix}&lt;br&gt;
1\\&lt;br&gt;
0&lt;br&gt;
\end{bmatrix}&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;把 $F(n+1)$ 和 $F(n)$ 開頭的兩行做合併之後可以得到更漂亮的寫法：&lt;/p&gt;
&lt;p&gt;$$&lt;br&gt;
\begin{bmatrix}&lt;br&gt;
F(n+1) &amp;amp; F(n)\\&lt;br&gt;
F(n) &amp;amp; F(n-1)&lt;br&gt;
\end{bmatrix} = \begin{bmatrix}&lt;br&gt;
1 &amp;amp; 1\\&lt;br&gt;
1 &amp;amp; 0&lt;br&gt;
\end{bmatrix}^{n-1} \begin{bmatrix}&lt;br&gt;
1 &amp;amp; 1\\&lt;br&gt;
1 &amp;amp; 0&lt;br&gt;
\end{bmatrix}&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;得到最後的公式：&lt;/p&gt;
&lt;p&gt;$$&lt;br&gt;
\begin{bmatrix}&lt;br&gt;
F(n+1) &amp;amp; F(n)\\&lt;br&gt;
F(n) &amp;amp; F(n-1)&lt;br&gt;
\end{bmatrix} = \begin{bmatrix}&lt;br&gt;
1 &amp;amp; 1\\&lt;br&gt;
1 &amp;amp; 0&lt;br&gt;
\end{bmatrix}^{n}&lt;br&gt;
$$&lt;/p&gt;
&lt;h2 id="矩陣的-n-次方真的可以-olg-n-嗎"&gt;矩陣的 n 次方真的可以 $O(\lg n)$ 嗎？&lt;/h2&gt;
&lt;p&gt;不同的例子不同，因為矩陣的乘法實際上並不會都是 $O(1)$，但是在這裡我們固定了矩陣的大小為 $2 \times 2$，只會對費氏數列的 n 做增長，因此矩陣乘法本身的複雜度並不會增長。&lt;/p&gt;
&lt;p&gt;這邊有個名詞是&lt;strong&gt;快速冪&lt;/strong&gt;，中文也許不好懂，英文就是 &lt;strong&gt;exponentiation by squaring&lt;/strong&gt;，就是把高次方的 power 用 2 的次方去組合。或著是說，用其二進位時數值是 1 的位元來做組合。&lt;br&gt;
因此算某數的 n 次方，我們只需要進行 $O(\lg n)$ 次計算即可。&lt;/p&gt;
&lt;p&gt;舉個簡單的例子，當計算 x 的 23 次方時，我們知道 23 的二進位可以表示成 10111，因為：&lt;/p&gt;
&lt;p&gt;$$&lt;br&gt;
23 = 2^4 + 2^2 + 2^1 + 2^0&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;所以實際上我們只要計算：&lt;/p&gt;
&lt;p&gt;$$&lt;br&gt;
x^{23} = x^{2^4 + 2^2 + 2^1 + 2^0} = x^{16}x^4x^2x&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;其中四個數值的次方計算為：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$x^{16} = x^8x^8$&lt;/li&gt;
&lt;li&gt;$x^{8} = x^4x^4$&lt;/li&gt;
&lt;li&gt;$x^{4} = x^2x^2$&lt;/li&gt;
&lt;li&gt;$x^{2} = xx$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;時間複雜度&lt;/strong&gt;和&lt;strong&gt;空間複雜度&lt;/strong&gt;都是 $O(\lg n)$&lt;/p&gt;
&lt;p&gt;拿來應用在矩陣上，就是所謂&lt;strong&gt;矩陣快速冪&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="說了這麼多給我-code-吧"&gt;說了這麼多，給我 code 吧&lt;/h2&gt;
&lt;p&gt;這裡參考了 &lt;a href="https://leetcode.com/problems/fibonacci-number/solution/"&gt;leetcode 的官方解法&lt;/a&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fibonacci&lt;/span&gt;(n):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; A &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;], [&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; power(A, n&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; A[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;][&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;power&lt;/span&gt;(A, n):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; n &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; A
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; q, r &lt;span style="color:#f92672"&gt;=&lt;/span&gt; divmod(n, &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; power(A, q)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; multiply(A, A)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; r &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; multiply(A, [[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;], [&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;]])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;multiply&lt;/span&gt;(A, B):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; x &lt;span style="color:#f92672"&gt;=&lt;/span&gt; A[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;][&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;] &lt;span style="color:#f92672"&gt;*&lt;/span&gt; B[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;][&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;] &lt;span style="color:#f92672"&gt;+&lt;/span&gt; A[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;][&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;] &lt;span style="color:#f92672"&gt;*&lt;/span&gt; B[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;][&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; y &lt;span style="color:#f92672"&gt;=&lt;/span&gt; A[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;][&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;] &lt;span style="color:#f92672"&gt;*&lt;/span&gt; B[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;][&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;] &lt;span style="color:#f92672"&gt;+&lt;/span&gt; A[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;][&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;] &lt;span style="color:#f92672"&gt;*&lt;/span&gt; B[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;][&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; z &lt;span style="color:#f92672"&gt;=&lt;/span&gt; A[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;][&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;] &lt;span style="color:#f92672"&gt;*&lt;/span&gt; B[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;][&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;] &lt;span style="color:#f92672"&gt;+&lt;/span&gt; A[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;][&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;] &lt;span style="color:#f92672"&gt;*&lt;/span&gt; B[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;][&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; w &lt;span style="color:#f92672"&gt;=&lt;/span&gt; A[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;][&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;] &lt;span style="color:#f92672"&gt;*&lt;/span&gt; B[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;][&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;] &lt;span style="color:#f92672"&gt;+&lt;/span&gt; A[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;][&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;] &lt;span style="color:#f92672"&gt;*&lt;/span&gt; B[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;][&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; A[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;][&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; x
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; A[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;][&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; y
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; A[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;][&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; z
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; A[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;][&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; w
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;因為要進行矩陣運算，我們必須另外實作相關 code，所以才多了 &lt;code&gt;power&lt;/code&gt; 和 &lt;code&gt;multiply&lt;/code&gt; 兩個 function。&lt;br&gt;
當然，也可以直接使用 numpy：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fibonacci&lt;/span&gt;(n):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 但我自己測試，這個會有精確度問題&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; A &lt;span style="color:#f92672"&gt;=&lt;/span&gt; np&lt;span style="color:#f92672"&gt;.&lt;/span&gt;array([[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;], [&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;]])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; np&lt;span style="color:#f92672"&gt;.&lt;/span&gt;linalg&lt;span style="color:#f92672"&gt;.&lt;/span&gt;matrix_power(A, n&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;以上就是費氏數列的 $O(\lg n)$ 矩陣解法。&lt;/p&gt;
&lt;p&gt;不過其實這個方法還能進一步改善，就留到&lt;a href="https://dwye.dev/post/python-fibonacci-3/"&gt;下一篇&lt;/a&gt;再說明吧。&lt;/p&gt;
&lt;h2 id="參考資料"&gt;參考資料：&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medium.com/fcamels-notes/%E8%B2%BB%E6%B0%8F%E6%95%B8%E5%88%97-o-logn-%E7%9A%84%E8%A7%A3%E6%B3%95-e36067e57baa"&gt;Medium 文章：費氏數列 O (LogN) 的解法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leetcode.com/problems/fibonacci-number/solution/"&gt;LeetCode 509. Fibonacci Number 的 Solution&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>Google Domain 客製化 Email Address</title><link>https://dwye.dev/post/google-domain-mail/</link><pubDate>Tue, 30 Mar 2021 16:01:56 +0800</pubDate><guid>https://dwye.dev/post/google-domain-mail/</guid><description>
&lt;p&gt;&lt;a href="https://dwye.dev/post/github-page-google-domain/"&gt;在 Google Domain 買了網域之後&lt;/a&gt;，就覺得該多利用一下這個新的個人網域 &lt;code&gt;dwye.dev&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;最簡單的事情就是做一個 email address。&lt;/p&gt;
&lt;p&gt;原本以為會需要配合 Google Workspace (原本的 G Suite，畢竟在 Google Domain 一直出現他的廣告&amp;hellip;)，後來才發現其實 Google Domain 可以直接免費設置轉信到現有信箱。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;還可以 100 筆。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;又是 100，跟之前 Blogger 的一個帳號最多 100 個 blog 一樣，100 是什麼 magic number 嗎？&lt;/p&gt;
&lt;h2 id="正文"&gt;正文&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;進入 Google Domain，選擇你的網域&lt;/li&gt;
&lt;li&gt;在側邊欄選擇電子郵件&lt;/li&gt;
&lt;li&gt;忽略 Google Workspace 的廣告，捲到底下有個「電子郵件轉寄」，直接給他新增一個電子郵件別名&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;然後&amp;hellip;就沒了ˊˋ&lt;/p&gt;
&lt;p&gt;&lt;img src="https://i.imgur.com/beV5sj3.png" alt="customized email address in google domain"&gt;&lt;/p&gt;
&lt;p&gt;用起來又是意外的簡單，感謝 Google Domain 讓我洗文章（Ｘ&lt;/p&gt;
&lt;p&gt;&lt;del&gt;可以去更新履歷了（不是才剛找完工作&lt;/del&gt;&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>Python 費氏數列解法（一）</title><link>https://dwye.dev/post/python-fibonacci/</link><pubDate>Sun, 28 Mar 2021 20:18:25 +0800</pubDate><guid>https://dwye.dev/post/python-fibonacci/</guid><description>
&lt;p&gt;面試被問到的題目，雖然是很基本的題目，但相關延伸也有不少，寫篇文章記錄一下。&lt;/p&gt;
&lt;h2 id="遞迴最基本又直觀的解法"&gt;遞迴：最基本又直觀的解法&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fibonacci&lt;/span&gt;(n):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; n &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; fibonacci(n&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) &lt;span style="color:#f92672"&gt;+&lt;/span&gt; fibonacci(n&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="時間複雜度"&gt;時間複雜度&lt;/h3&gt;
&lt;p&gt;$O(2^n)$，每次呼叫 &lt;code&gt;fibonacci(n)&lt;/code&gt; 都會進行額外兩次呼叫，n 每次會 -1，直到 n == 2。整個呼叫會是高度 n 的二元樹的子集合，而每次運行時間都是 $O(1)$，因此整體時間的 upper bound 就是二元樹的節點數量：&lt;/p&gt;
&lt;p&gt;$$&lt;br&gt;
2^0 + 2^1 + 2^2 + &amp;hellip; + 2^{n-1} = \frac{2^n - 1}{2 - 1}&lt;br&gt;
$$&lt;/p&gt;
&lt;h3 id="空間複雜度"&gt;空間複雜度&lt;/h3&gt;
&lt;p&gt;$O(1)$&lt;/p&gt;
&lt;h2 id="遞迴優化利用-hash-減少計算次數"&gt;遞迴優化：利用 Hash 減少計算次數&lt;/h2&gt;
&lt;p&gt;在原本的解法中，會有很多次重複計算 &lt;code&gt;fibonacci(n-1)&lt;/code&gt;, &lt;code&gt;fibonacci(n-2)&lt;/code&gt; &amp;hellip; 等，因此可以把計算結果儲存起來，&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fibonacci&lt;/span&gt;(n):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; table &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fib_table&lt;/span&gt;(n):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; n &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; table[n] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; fib_table(n&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) &lt;span style="color:#f92672"&gt;+&lt;/span&gt; fib_table(n&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; table[n]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; fib_table(n)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這邊我利用了 Python 變數 scope 的 LEGB 特性，也就是查找順序為：local, enclosed, global, built-in。可以參見&lt;a href="https://docs.python.org/3/tutorial/classes.html"&gt;官方 document&lt;/a&gt;，有趣的是 LEGB 並不是官方名詞而是社群流傳的口訣。&lt;/p&gt;
&lt;p&gt;也許聽起來很複雜，但其實也就是簡單的&lt;strong&gt;往外查找&lt;/strong&gt;規則罷了。&lt;/p&gt;
&lt;p&gt;因此 &lt;code&gt;table&lt;/code&gt; 雖然是在外部的 &lt;code&gt;fibonacci&lt;/code&gt; 定義的，但 &lt;code&gt;fib_table&lt;/code&gt; 內（local）沒有另外定義 &lt;code&gt;table&lt;/code&gt; 變數的情況下，會往外查找，因此可以讀取外部的 &lt;code&gt;table&lt;/code&gt;（enclosed）。&lt;/p&gt;
&lt;p&gt;可能有人會問，我有 assign 值到 &lt;code&gt;table&lt;/code&gt; 內部啊，這樣不用宣告 &lt;code&gt;global table&lt;/code&gt; 嗎？答案是不需要的，在 Python 中，&lt;code&gt;table&lt;/code&gt; 變數存的是物件的 reference，因此改變物件內的屬性並不會影響 &lt;code&gt;table&lt;/code&gt; 這個變數。而 &lt;code&gt;table[n] = fib_table(n-1) + fib_table(n-2)&lt;/code&gt; 這行實際上是呼叫了 &lt;code&gt;table&lt;/code&gt; 指向的物件的 &lt;code&gt;__setitem__&lt;/code&gt; 方法，改變物件內部的屬性。&lt;/p&gt;
&lt;h3 id="時間複雜度-1"&gt;時間複雜度&lt;/h3&gt;
&lt;p&gt;$O(n)$，對每個 n 只需要算一遍，不會每次都要往 n-1，n-2 展開。&lt;/p&gt;
&lt;h3 id="空間複雜度-1"&gt;空間複雜度&lt;/h3&gt;
&lt;p&gt;$O(n)$，&lt;code&gt;table&lt;/code&gt; 需要存 &lt;code&gt;fib_table(3)&lt;/code&gt; 到 &lt;code&gt;fib_table(n)&lt;/code&gt; 的結果。&lt;/p&gt;
&lt;h2 id="遞迴優化使用-cache"&gt;遞迴優化：使用 Cache&lt;/h2&gt;
&lt;p&gt;Python 有內建的 Cache 可以記錄函式呼叫結果：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@cache&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fibonacci&lt;/span&gt;(n):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; n &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; fibonacci(n&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) &lt;span style="color:#f92672"&gt;+&lt;/span&gt; fibonacci(n&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這其實就是第一個解法加上一行 &lt;code&gt;@cache&lt;/code&gt; 而已。&lt;/p&gt;
&lt;p&gt;在函式開頭加上 &lt;code&gt;@something&lt;/code&gt; 的寫法是呼叫 Python 中的 decorator，是 Python 中一個重要的特性。在 Python 語言中，函式是一等公民，也就是函式可以被當作變數 assign，也可以當作參數傳遞。 decorator 實際上就是把他裝飾的函式傳到 decorator 內部再做一些事情，以這邊的 &lt;code&gt;@cache&lt;/code&gt; 為例，decorator 會自動幫忙把 &lt;code&gt;fibonacci&lt;/code&gt; 這個函式的呼叫結果記錄起來，並回傳&lt;strong&gt;包裝後的函數&lt;/strong&gt;，我們最後拿到的 &lt;code&gt;fibonacci&lt;/code&gt; 實際上會多了包裝後的方法：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; fibonacci&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;__dir__&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;cache_parameters&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;cache_info&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;cache_clear&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;比自己用 hash 實作簡單多了。&lt;/p&gt;
&lt;p&gt;這邊的 &lt;code&gt;cache&lt;/code&gt; 實際上是 &lt;code&gt;functools.lru_cache(maxsize=None)&lt;/code&gt;，一個不限制大小的 LRU Cache，如果想要限制大小可以在修改 &lt;code&gt;maxsize&lt;/code&gt; 發揮 LRU 的功能。&lt;/p&gt;
&lt;p&gt;學會了這招才是真正的 Pythonist。&lt;/p&gt;
&lt;h3 id="cache-size-要設定成多少"&gt;Cache Size 要設定成多少？&lt;/h3&gt;
&lt;p&gt;既然可以設定 cache size，我們真的需要一直記著每次計算的結果嗎？&lt;/p&gt;
&lt;p&gt;當計算第 n 項時，只需要 n-1 和 n-2 兩項結果，所以就設成 2 就好了對吧？但如果直接這樣改，會發現程式跑的時間還是會變很長。&lt;/p&gt;
&lt;p&gt;實際上當計算 n-1 時，需要知道 n-2 和 n-3 的值，先計算 n-2 後計算 n-3。因此最後 cache 內記錄的會是 n-1 和 n-3 的值。&lt;br&gt;
所以會在接下來算 n-2 時造成一個 cache miss。&lt;/p&gt;
&lt;p&gt;如果不改其他部分，可以把 &lt;code&gt;maxsize&lt;/code&gt; 設成 3，就可以避免掉這個 cache miss。&lt;/p&gt;
&lt;p&gt;但仔細思考，其實會有這個 miss，歸根究底是我們先計算比較靠近 n 的 n-1，後計算比較遠離 n 的 n-2 造成的。那何不將兩者交換呢？&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@lru_cache&lt;/span&gt;(maxsize&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fibonacci&lt;/span&gt;(n):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; n &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; fibonacci(n&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;) &lt;span style="color:#f92672"&gt;+&lt;/span&gt; fibonacci(n&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;做完簡單的修改，一樣漂亮的 code，可以給出不錯的時間複雜度和常數的空間複雜度。&lt;/p&gt;
&lt;h3 id="時間複雜度-2"&gt;時間複雜度&lt;/h3&gt;
&lt;p&gt;$O(n)$，不會有 cache miss，每個 n 只需要計算一次。&lt;/p&gt;
&lt;h3 id="空間複雜度-2"&gt;空間複雜度&lt;/h3&gt;
&lt;p&gt;$O(1)$，因為 cache size 最大是 2。&lt;/p&gt;
&lt;h2 id="尾遞迴-tail-recursive--tail-call"&gt;尾遞迴 Tail Recursive / Tail Call&lt;/h2&gt;
&lt;p&gt;當函式本身在回傳時會呼叫自己即為尾遞迴，可以說是遞迴中的特例。&lt;/p&gt;
&lt;p&gt;要撰寫尾遞迴，需要思考每次遞迴時我們往前取了什麼資訊。在上一個 LRU cache 的解法中知道，我們只需要紀錄 n-2 和 n-1 兩個值即可。分別命名為 &lt;code&gt;f1&lt;/code&gt; 和 &lt;code&gt;f2&lt;/code&gt;，每次呼叫我們把 n 減 1，把 &lt;code&gt;f1&lt;/code&gt; 指定為 &lt;code&gt;f2&lt;/code&gt;，並把 &lt;code&gt;f2&lt;/code&gt; 改為 &lt;code&gt;f1 + f2&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fibonacci&lt;/span&gt;(n):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fib_tail_recursive&lt;/span&gt;(n, f1, f2):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; n &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; f2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; fib_tail_recursive(n &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, f2, f1 &lt;span style="color:#f92672"&gt;+&lt;/span&gt; f2)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; fib_tail_recursive(n, &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;尾遞迴的版本我認為比較不好理解，但是為了下一個迭代法，還是練習了一下尾遞迴的解法。&lt;/p&gt;
&lt;p&gt;下面對於不同的 n 做分析：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;初始時 &lt;code&gt;f1&lt;/code&gt; 和 &lt;code&gt;f2&lt;/code&gt; 分別代表第一項和第二項，都是 1。&lt;/li&gt;
&lt;li&gt;當 n &amp;lt; 3 時直接回傳 &lt;code&gt;f1&lt;/code&gt; 為 1&lt;/li&gt;
&lt;li&gt;當 n = 3 時，會跑一次尾遞迴，此時 n 變成 2，&lt;code&gt;f1&lt;/code&gt; 變成 1， &lt;code&gt;f2&lt;/code&gt; 變成 1 + 1 = 2，接著回傳 &lt;code&gt;f2&lt;/code&gt; 即為 2&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以下依此類推&amp;hellip;&lt;/p&gt;
&lt;h3 id="時間複雜度-3"&gt;時間複雜度&lt;/h3&gt;
&lt;p&gt;$O(n)$，從 n 呼叫到 2。&lt;/p&gt;
&lt;h3 id="空間複雜度-3"&gt;空間複雜度&lt;/h3&gt;
&lt;p&gt;$O(1)$，沒有額外的 cache 或是查表。&lt;/p&gt;
&lt;h2 id="迭代法使用迴圈攤平遞迴"&gt;迭代法：使用迴圈，攤平遞迴&lt;/h2&gt;
&lt;p&gt;尾遞迴是攤平遞迴的中間做法，可以寫成尾遞迴的函式，就能快速改寫成迭代形式：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fibonacci&lt;/span&gt;(n):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; f1, f2 &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; i &lt;span style="color:#f92672"&gt;in&lt;/span&gt; range(&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;, n): &lt;span style="color:#75715e"&gt;# 當 n &amp;lt; 3 時不會進入迴圈&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; f1, f2 &lt;span style="color:#f92672"&gt;=&lt;/span&gt; f2, f1 &lt;span style="color:#f92672"&gt;+&lt;/span&gt; f2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; f2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;做的事情和尾遞迴版本一模一樣。&lt;/p&gt;
&lt;p&gt;也因此有些語言對於尾遞迴有做優化，當編譯器看到尾遞迴，會自動幫你攤平成迴圈（用 GOTO 實現），稱為尾遞迴優化（Tail Call Optimization）。主要會在以 functional programming 為主的語言中看到，例如 erlang 或是 haskell。&lt;/p&gt;
&lt;p&gt;很可惜，Python 是沒有支援的那部分。&lt;/p&gt;
&lt;h3 id="時間複雜度-4"&gt;時間複雜度&lt;/h3&gt;
&lt;p&gt;$O(n)$，只有一個迴圈，迴圈內部運算都是 $O(1)$&lt;/p&gt;
&lt;h3 id="空間複雜度-4"&gt;空間複雜度&lt;/h3&gt;
&lt;p&gt;$O(1)$，只用了固定兩個變數&lt;/p&gt;
&lt;h2 id="其他解法"&gt;其他解法&lt;/h2&gt;
&lt;p&gt;雖然到這邊已經說明完正常的解法，但其實費氏數列還有更多有趣的解法。&lt;/p&gt;
&lt;p&gt;時間複雜度更短的 $O(\lg n)$ 的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dwye.dev/post/python-fibonacci-2/"&gt;矩陣解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwye.dev/post/python-fibonacci-3/"&gt;快速冪&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwye.dev/post/python-fibonacci-4/"&gt;疑似（？）常數時間但其實最多也只能到 $O(\lg n)$ 的公式解&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以及&lt;a href="https://dwye.dev/post/python-fibonacci-5/"&gt;所有解法的執行時間比較&lt;/a&gt;&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>GitHub Page + Google Domain 設定個人化網域</title><link>https://dwye.dev/post/github-page-google-domain/</link><pubDate>Sat, 27 Mar 2021 19:01:56 +0800</pubDate><guid>https://dwye.dev/post/github-page-google-domain/</guid><description>
&lt;p&gt;以前在 KK 實習時，跟 RD 們聊過自架 blog 的事情，並聽說了這個很潮的 &lt;code&gt;.dev&lt;/code&gt; 網域，就把購買個人網域列為將來想嘗試的事之一。&lt;br&gt;
找工作的事情告一段落了（其實也只是在原本實習的公司轉正職 XD），有了固定收入就可以來養個人品牌了 XD。&lt;/p&gt;
&lt;h2 id="需要做的事情"&gt;需要做的事情&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;購買一個你喜歡的個人網域，我在 Google Domain 購買&lt;/li&gt;
&lt;li&gt;在 Google Domain 設定你的 GitHub Page url&lt;/li&gt;
&lt;li&gt;在 GitHub Repo 設定你的個人網域以及 HTTPS&lt;/li&gt;
&lt;li&gt;修改 Repo 內相關設定&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="google-domain"&gt;Google Domain&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://domains.google.com/"&gt;https://domains.google.com/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Google 自家的網域供應商，也提供了 DNS 服務，Google 雖然 do evil 但他們家服務的可靠性還是直得讚賞的，也不會跟你搞奇怪的首年特價第二年漲價。我二話不說直接選了 Google Domain 來購買個人網域。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;dwye.dev&lt;/code&gt;，就決定是你了！&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.dev&lt;/code&gt; 網域的一年價格是 12 USD，大約 350 台幣附近，其他網域結尾的價格可以參考 &lt;a href="https://support.google.com/domains/answer/6010092"&gt;Google 的說明&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;比較需要注意的是 Google Domain 目前沒有開放台灣地區購買，但也不限制你用外幣結帳，但就要付跨區交易的手續費。&lt;/p&gt;
&lt;p&gt;中間會另外需要填寫一次給 ICANN（網際網路名稱與數字位址分配機構）的個人資訊，這些資訊不會被公開，最好填寫真實的（就算跨區了，寫台灣也沒問題）。&lt;/p&gt;
&lt;h2 id="google-domain-設定"&gt;Google Domain 設定&lt;/h2&gt;
&lt;p&gt;購買完成之後，回到 Google Domain 就會出現「my domain」選項，為了連接到 GitHub Page，需要做一些 DNS 資源設定：&lt;/p&gt;
&lt;h3 id="base-domain-apex-domain"&gt;Base Domain (Apex Domain)&lt;/h3&gt;
&lt;p&gt;為了支援 GitHub Page 的 HTTPS 設定，根據&lt;a href="https://docs.github.com/en/github/working-with-github-pages/managing-a-custom-domain-for-your-github-pages-site#configuring-a-records-with-your-dns-provider"&gt;官方文件&lt;/a&gt;，需要在 DNS 中將 base domain 設定至下列 IP：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;185.199.108.153
185.199.109.153
185.199.110.153
185.199.111.153
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;具體做法是在 DNS 頁面中下方 Custom resource records 中，選擇 resource type: A，並新增上述四組 IP：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://i.imgur.com/1P3zHXX.png" alt="DNS apex domain setting"&gt;&lt;/p&gt;
&lt;p&gt;DNS 設定需要一段時間生效，Google Domain 算是很快的，只讓我等了幾分鐘而已，下列指令可以確定剛剛的設定是否生效：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ dig dwye.dev +nostats +nocomments +nocmd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;; &amp;lt;&amp;lt;&amp;gt;&amp;gt; DiG 9.10.6 &amp;lt;&amp;lt;&amp;gt;&amp;gt; dwye.dev +nostats +nocomments +nocmd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;; global options: +cmd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;;dwye.dev. IN A
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dwye.dev. 3599 IN A 185.199.111.153
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dwye.dev. 3599 IN A 185.199.109.153
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dwye.dev. 3599 IN A 185.199.108.153
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dwye.dev. 3599 IN A 185.199.110.153
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;出現了上面的 IP 代表生效了。&lt;/p&gt;
&lt;h3 id="cname"&gt;CNAME&lt;/h3&gt;
&lt;p&gt;CNAME (canonical name) 就是真實名稱的意思，在 DNS 這裡的作用是將個人化網域 map 到真實網域，以我的例子來說，要將 &lt;code&gt;dwye.dev&lt;/code&gt; map 到 &lt;code&gt;dwy6626.github.io&lt;/code&gt;，因此一樣在 DNS 頁面中下方 Custom resource records 中，選擇 resource type: DNS，並輸入真實名稱 &lt;code&gt;dwy6626.github.io&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://i.imgur.com/CfdasYG.png" alt="DNS CNAME setting"&gt;&lt;/p&gt;
&lt;h2 id="github-repo-設定"&gt;GitHub Repo 設定&lt;/h2&gt;
&lt;p&gt;這邊是架設 GitHub Page 的 repo，以我的例子來說是這個 repo：&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwy6626/dwy6626.github.io"&gt;https://github.com/dwy6626/dwy6626.github.io&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;在 Setting 第一頁找到 &lt;strong&gt;GitHub Pages&lt;/strong&gt; 設定，並在 custom domain 填入個人化網域 &lt;code&gt;dwye.dev&lt;/code&gt;，就這麼簡單。&lt;/p&gt;
&lt;p&gt;不過如果你做得太快，Google Domain 的 DNS 設定還沒生效的話，會暫時無法使用 HTTPS，一樣等個幾分鐘再回來打勾就好。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://i.imgur.com/kcZgbaZ.png" alt="GitHub Repo Setting Custom Domain"&gt;&lt;/p&gt;
&lt;p&gt;到這邊基本上大致上就完成了，然而修改完了設定之後，code base 以及 CI/CD 也有需要修改的地方，這樣才能保證下次部署後資料是正常的。&lt;/p&gt;
&lt;h2 id="repo-內修改"&gt;Repo 內修改&lt;/h2&gt;
&lt;p&gt;我是用 &lt;a href="https://dwye.dev/hugo-github-action"&gt;Hugo + GitHub Action&lt;/a&gt; 產生並透過 CI/CD 自動部署靜態網站，裡面也有相關的設定需要修改：&lt;/p&gt;
&lt;h3 id="hugo"&gt;Hugo&lt;/h3&gt;
&lt;p&gt;修改 config 中 baseURL 設定：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# config.toml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;baseURL&lt;/span&gt; = &lt;span style="color:#e6db74"&gt;&amp;#34;https://dwye.dev/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="github-action"&gt;GitHub Action&lt;/h3&gt;
&lt;p&gt;因為我使用的是 &lt;code&gt;peaceiris/actions-gh-pages@v3&lt;/code&gt; 這個 Action，已經內建支援設定 CNAME 了，只需要加上去就好：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# .github/workflows/deploy.yml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Deploy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;uses&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;peaceiris/actions-gh-pages@v3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;external_repository&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;dwy6626/dwy6626.github.io&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;deploy_key&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;${{ secrets.ACTIONS_DEPLOY_KEY }}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;publish_dir&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;./public&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;publish_branch&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;master&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;cname&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;dwye.dev&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;最後那行即是 CNAME 設定，部署完成後就會自動產生一個 CNAME 檔案在 GitHub Page Repo 內。&lt;/p&gt;
&lt;h2 id="結語"&gt;結語&lt;/h2&gt;
&lt;p&gt;完成上面所有步驟之後，就可以連去你的網誌看看了，網址變短變的有個性，就是爽 XDDD&lt;/p&gt;
&lt;p&gt;所以個人化網域，其實就是花錢買個爽（Ｘ），當然還有個人品牌經營的部分，但對我來說比較是個人興趣吧。&lt;/p&gt;
&lt;p&gt;留一些 TO-DO：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Google Search Console 以及 Google Analytics 更新（應該會寫在下篇）&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwye.dev/post/google-domain-mail/"&gt;個人化 email 網址&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>GitHub Action 自動化部署 Hugo 網站</title><link>https://dwye.dev/post/hugo-github-action/</link><pubDate>Sun, 14 Mar 2021 22:01:56 +0800</pubDate><guid>https://dwye.dev/post/hugo-github-action/</guid><description>
&lt;p&gt;在今天之前，這個 blog 是&lt;a href="https://dwye.dev/post/hugo-wercker/"&gt;用 Wercker 做自動化部署&lt;/a&gt;，當時對於 CI/CD 工具沒有太多比較，就直接使用了。&lt;/p&gt;
&lt;p&gt;在 2018 年底，&lt;a href="https://github.blog/2018-10-16-future-of-software/"&gt;GitHub 公開了他們的自有 CI/CD 工具 GitHub Action&lt;/a&gt;，並於 2019 年底正式推出。這個工具經過了兩年多的發展，如今也有一定成熟度，社群有很多相關的工具發展起來，當然也不乏 Hugo 的自動化部署。如果 repo 使用 GitHub，那整合 GitHub 自家 CI/CD 當然是最方便的。&lt;/p&gt;
&lt;p&gt;因此去年在公司實習時，主管就叫我研究一下 GitHub Action 套用在當時開發的 Rails 專案，我只參考了官方文件並套用了一些社群貢獻的 GitHub Action，就成功完成任務，包含在開 PR 時自動跑 coding style review (rubocop) 以及 push 時跑單元測試 (RSpec)。&lt;del&gt;人生又往 YAML 工程師前進了一步。&lt;/del&gt;&lt;/p&gt;
&lt;h2 id="正文"&gt;正文&lt;/h2&gt;
&lt;p&gt;這邊就不多介紹 GitHub Action 了，接下來就記錄我如何從 Wercker 搬遷到 GitHub Action（說是搬遷，實際上就是參考原本的 CI/CD 建立一個新的 CI/CD）。&lt;/p&gt;
&lt;p&gt;首先我們先來看之前 wercker 設定檔：&lt;/p&gt;
&lt;script src="https://gist.github.com/dwy6626/d072c4809db1e04e02e4eab9365e567e.js"&gt;&lt;/script&gt;
&lt;p&gt;把 CI/CD 自動化的事情整理一下：&lt;/p&gt;
&lt;p&gt;每次 Push 時：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;更新 submodule 把 theme 拉下來（這邊是我修改自 Blackburn 主題的 &lt;a href="https://github.com/dwy6626/dw-favored-blackburn"&gt;dw-favored-blackburn&lt;/a&gt;）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hugo&lt;/code&gt; 產生靜態網站&lt;/li&gt;
&lt;li&gt;把 build 後在 &lt;code&gt;./public&lt;/code&gt; 資料夾內的檔案推到另一個公開 repo&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;對應的 GitHub Action 如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/actions/checkout"&gt;actions/checkout&lt;/a&gt;，官方 action，checkout repo 讓 workflow 可以存取 code，這邊可以直接設定 checkout 後要連 submodule 一起拉下來&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/peaceiris/actions-hugo"&gt;peaceiris/actions-hugo&lt;/a&gt;，幫你裝 Hugo（之後還是要自己跑 &lt;code&gt;hugo&lt;/code&gt; 來產生靜態網站）&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/peaceiris/actions-gh-pages"&gt;peaceiris/actions-gh-pages&lt;/a&gt;，幫你把某個資料夾下的檔案推到特定分支，也支援推到別的 GitHub repo，完全符合我的需求，只是需要另外在外部 repo 設定 deploy key 讓 action 可以把檔案推過去&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;把這三個照著 &lt;a href="https://docs.github.com/en/actions/reference"&gt;workflow syntax&lt;/a&gt; 寫好之後其實就大功告成了：&lt;/p&gt;
&lt;script src="https://gist.github.com/dwy6626/da652c1b3bb54651064c42d407abbebe.js"&gt;&lt;/script&gt;
&lt;p&gt;雖然整體看起來比原本長，不過對於觸發時機以及觸發分支的管理（on push, master）也集中到這個檔案了，就不用再 GitHub 和 Wercker 兩邊跑，全部集中在這個檔案（除了設定一些敏感資訊之外），因此我覺得還是比較方便。&lt;/p&gt;
&lt;p&gt;當 push 到 master 分支之後結果如下：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://i.imgur.com/XovsZ48.png" alt="workflow results"&gt;&lt;/p&gt;
&lt;h2 id="關於-deploy-key"&gt;關於 Deploy Key&lt;/h2&gt;
&lt;p&gt;這邊是稍微比較複雜的地方，這個 deploy key 是需要自己產生自己放上 GitHub 的。產生的方法不外乎使用 &lt;code&gt;ssh-keygen&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ ssh-keygen
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;照著指示做就好，之後會生成一組 public-private key。Private key 是沒有副檔名的，public key 則有附檔名 &lt;code&gt;.pub&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;Hugo 原始碼的 repo 要放 private key，放在 action secrets 供 action 存取（注意要把整個檔案內容，包含頭尾兩行 &lt;code&gt;-----BEGIN OPENSSH PRIVATE KEY-----&lt;/code&gt; 等一起貼上去）：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://i.imgur.com/bx9WQK9.png" alt="put private key in hugo repo’s action secret"&gt;&lt;/p&gt;
&lt;p&gt;要架設 GitHub Page 的外部 repo 則是要設定 deploy key，把 public key 整個檔案貼過去：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://i.imgur.com/HP3EQBB.png" alt="put public key to outer repo’s deploy key"&gt;&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>Python 實作 Disjoint Set 與 Union Find</title><link>https://dwye.dev/post/python-union-find/</link><pubDate>Mon, 22 Feb 2021 18:41:25 +0800</pubDate><guid>https://dwye.dev/post/python-union-find/</guid><description>
&lt;h2 id="使用情境"&gt;使用情境&lt;/h2&gt;
&lt;p&gt;在 Leetcode 寫到一題：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://leetcode.com/problems/number-of-operations-to-make-network-connected/"&gt;1319. Number of Operations to Make Network Connected&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;現在有 n 台電腦以及一些 cables 將電腦點對點連接，問需要移動至少幾條 cable 才能讓在所有電腦連成單一網路。&lt;br&gt;
以 graph 的角度來看，電腦就是 nodes，cables 就是 edges。&lt;/p&gt;
&lt;p&gt;要將整張 graph 連接起來，至少需要 n-1 個 edges。若一個 graph 裡面有超過 n-1 個 edges，剩下的就是多出來的 edges，可以供我們拿來移動的 edges。&lt;br&gt;
所以第一件事就是要&lt;strong&gt;檢查 edges 數量 &amp;gt;= n-1&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;當檢查完畢之後，我們有至少 n-1 條 edges，一定可以用這些 edges 將所有 nodes 連接起來。&lt;br&gt;
因為題目只問需要移動幾條 edges，我們可以假設我們移動的都是多出來的 edges，不必去動原本的 n-1 個 edges。&lt;/p&gt;
&lt;p&gt;假設原本的 graph 被切分成分離的 m 塊 connected components，則我們需要移動 m-1 個 edges 去連接，因此問題變成了&lt;strong&gt;找出目前有幾塊 connected components&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;這個問題的一個標準做法就是使用 disjoint set：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;makeConnected&lt;/span&gt;(self, n: int, connections: List[List[int]]) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; int:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 檢查 edges 數量 &amp;gt;= n-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; len(connections) &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; n&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 找出目前有幾塊 connected components&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ds &lt;span style="color:#f92672"&gt;=&lt;/span&gt; DisjointSet(range(n))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; u, v &lt;span style="color:#f92672"&gt;in&lt;/span&gt; connections:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ds&lt;span style="color:#f92672"&gt;.&lt;/span&gt;union(u, v)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; len(ds) &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="實作-disjoint-set"&gt;實作 Disjoint Set&lt;/h2&gt;
&lt;p&gt;Disjoint set 的特性，是將一個大集合裡面分為 n 個子集合，這些子集合本身是 disjoint，無交集的。並提供兩個方法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;find/1&lt;/code&gt;：查找某個元素在哪個集合，實務上會選擇其中一個「家長」當作代表&lt;/li&gt;
&lt;li&gt;&lt;code&gt;union/2&lt;/code&gt;：將兩個元素所在的集合合併&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在上個部分，我假設 &lt;code&gt;DisjointSet&lt;/code&gt; 已經寫好了，而我們要實作的則是 initialization 和 &lt;code&gt;union&lt;/code&gt; 方法，而 &lt;code&gt;union&lt;/code&gt; 會需要查找輸入的元素所在的集合，因此 &lt;code&gt;find&lt;/code&gt; 當然也必須實作。&lt;/p&gt;
&lt;p&gt;教科書做法是使用 set forest 實作，也就是每個子集合都是一個 tree，每個 node 只需要一個 pointer 指向其 parent，root 為「家長」。這樣上面的方法所做的事情就是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;find/1&lt;/code&gt;：一路網上查找「家長」&lt;/li&gt;
&lt;li&gt;&lt;code&gt;union/2&lt;/code&gt;：將兩棵樹合併，找到家長之後，將其中一個的 parent 指向另一個&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果是這樣的話我們可以這樣實作：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;DisjointSet&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__init__&lt;/span&gt;(self, elements):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;parents &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [n &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; n &lt;span style="color:#f92672"&gt;in&lt;/span&gt; elements]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;count &lt;span style="color:#f92672"&gt;=&lt;/span&gt; len(self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;parents)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;find&lt;/span&gt;(self, element):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; n &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;parents[element]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;parents[n] &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; n:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; n &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;parents[n]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; n
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;union&lt;/span&gt;(self, u, v):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;find(u)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; v &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;find(v)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; u &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; v:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;parents[u] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; v
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;count &lt;span style="color:#f92672"&gt;-=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__len__&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;count
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="進一步優化"&gt;進一步優化&lt;/h2&gt;
&lt;p&gt;但其實這個物件還有可以優化的地方：&lt;code&gt;find&lt;/code&gt; 會重複執行，如果 tree 很深，&lt;code&gt;find&lt;/code&gt; 的時間就會越來越長。由於 node 在 tree 內部的位置並不是重點，我們希望能夠讓向上查找家長這件事變快，也就是保持 tree 越淺越好。為此可以做兩件事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;加入 rank 概念&lt;/strong&gt;，代表該子集合的最大可能深度，保持每個 set 深度的平衡，避免某個 tree 的深度太高。把 rank 記錄在家長的 node 即可&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Path compression&lt;/strong&gt;，有點像是把每次 &lt;code&gt;find&lt;/code&gt; 的結果存起來，做法是當 &lt;code&gt;find&lt;/code&gt; 做完時，把沿路找到的 node 都掛載家長下方，這樣下次在這個子集合裡跑 find 時就會加速許多。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以 &lt;code&gt;DisjointSet&lt;/code&gt; 改寫為：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;DisjointSet&lt;/span&gt;: &lt;span style="color:#75715e"&gt;# with rank and path compression&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__init__&lt;/span&gt;(self, elements):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sets &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [Node() &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; n &lt;span style="color:#f92672"&gt;in&lt;/span&gt; elements]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;count &lt;span style="color:#f92672"&gt;=&lt;/span&gt; len(self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sets)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;find&lt;/span&gt;(self, element):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_find(self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sets[element])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;_find&lt;/span&gt;(self, n):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; n&lt;span style="color:#f92672"&gt;.&lt;/span&gt;parent &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; n:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# path compression&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; n&lt;span style="color:#f92672"&gt;.&lt;/span&gt;parent &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_find(n&lt;span style="color:#f92672"&gt;.&lt;/span&gt;parent)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; n&lt;span style="color:#f92672"&gt;.&lt;/span&gt;parent
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;union&lt;/span&gt;(self, u, v):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;find(u)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; v &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;find(v)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; u &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; v:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 把 rank 小的掛到 rank 大的下方&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; u&lt;span style="color:#f92672"&gt;.&lt;/span&gt;rank &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; v&lt;span style="color:#f92672"&gt;.&lt;/span&gt;rank:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u&lt;span style="color:#f92672"&gt;.&lt;/span&gt;parent &lt;span style="color:#f92672"&gt;=&lt;/span&gt; v
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; v&lt;span style="color:#f92672"&gt;.&lt;/span&gt;parent &lt;span style="color:#f92672"&gt;=&lt;/span&gt; u
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; v&lt;span style="color:#f92672"&gt;.&lt;/span&gt;rank &lt;span style="color:#f92672"&gt;==&lt;/span&gt; u&lt;span style="color:#f92672"&gt;.&lt;/span&gt;rank:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; u&lt;span style="color:#f92672"&gt;.&lt;/span&gt;rank &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;count &lt;span style="color:#f92672"&gt;-=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__len__&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;count
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這邊用到了 Node 物件，其實就是 C 的 struct 的概念。其實也可以只使用 tuple，但我選擇寫成物件比較清楚：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Node&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;__init__&lt;/span&gt;(self):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;parent &lt;span style="color:#f92672"&gt;=&lt;/span&gt; self
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self&lt;span style="color:#f92672"&gt;.&lt;/span&gt;rank &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;以 leetcode 實測，前後執行時間分別為 1748 ms 及 560 ms，相差了三倍。&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>Clean Code 心得</title><link>https://dwye.dev/post/clean-code/</link><pubDate>Tue, 08 Sep 2020 00:41:25 +0800</pubDate><guid>https://dwye.dev/post/clean-code/</guid><description>
&lt;p&gt;因為公司有這本書的中文版，在各種等待時間，慢慢把這本看完了，同時也一邊寫些自己認為的重點。&lt;/p&gt;
&lt;p&gt;其實我覺得 Clean Code 適合的閱讀對象，是從來沒有接觸過任何 Coding Style 的人。&lt;/p&gt;
&lt;p&gt;如果你跟我一樣，有和別人合作過，平常有遵守一些準則（例如 PEP8），或是使用包含 Styling 的 linter (pylint / eslint / rubocop &amp;hellip;)，這本書還是能找到能學習的地方，例如命名原則、封裝的部分、模組的拆分、測試撰寫等等，這些開發準則以及自動化的 formatter 沒辦法告訴你的部分。&lt;/p&gt;
&lt;p&gt;也難怪第一次聽到別人推薦大家看這本書的時候，把它歸類在「選讀」，卻又說裡面的內容&lt;strong&gt;很重要&lt;/strong&gt;。因為很多觀念在現在已經是日常了。&lt;/p&gt;
&lt;p&gt;比較後面的章節，從系統、平行化開始，討論了一些比較深入的知識，Java code 也開始變難懂了，而持續精鍊、JUnit 等章節，就真的是針對 Java 的實例做說明，而非原則介紹的部分，這些對我來說讀起來就比較吃力一點。而平常會聽到大家討論的，也都是前面章節討論一些基本原則的部分。因此對於時間比較少的人或許可以先點到為止。&lt;/p&gt;
&lt;p&gt;&lt;del&gt;我看網路上的心得也很少人真的讀完後面 XD&lt;/del&gt;&lt;/p&gt;
&lt;h3 id="列舉一下我覺得比較重要的章節"&gt;列舉一下我覺得比較重要的章節：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Chp. 1 ~ Chp.10&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Chp. 12&lt;/strong&gt; 簡單的總結&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Chp. 17&lt;/strong&gt; 一個清單，其實包含了 Chp. 14-16 的一些啟發&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;我認為重要順序是倒著的，完全沒時間可以先看 Chp. 17&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;另外 Chp. 11 其實也蠻重要的，但是裡面太多 Java 的部分了，如果以後學 Java 再來細看吧。&lt;/p&gt;
&lt;p&gt;大概是這樣，下面就附上我紀錄的每章節重點吧。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="前言"&gt;前言&lt;/h2&gt;
&lt;p&gt;Clean code 是一門學派，不是絕對正確&lt;/p&gt;
&lt;p&gt;減少閱讀程式碼的時間&lt;/p&gt;
&lt;p&gt;雜亂程式會拖累開發時間&lt;/p&gt;
&lt;p&gt;讓程式清楚明白，不要隱藏意圖，避免 &lt;a href="https://zh.wikipedia.org/wiki/%E5%8F%8D%E9%9D%A2%E6%A8%A1%E5%BC%8F"&gt;anti-pattern&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;童子軍規則：離開營地前，讓營地比使用前更乾淨&lt;/p&gt;
&lt;h2 id="變數命名"&gt;變數命名&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;類別：名詞，方法：動詞&lt;/li&gt;
&lt;li&gt;避免無意義的字：the, variable, name&lt;/li&gt;
&lt;li&gt;避免跟形態衝突的字（會誤導），除非他真的是那個形態：list, array
&lt;ul&gt;
&lt;li&gt;或是省去：&lt;code&gt;Accounts&lt;/code&gt; &amp;gt; &lt;code&gt;AccountList&lt;/code&gt;&lt;/li&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;li&gt;e.g. &lt;code&gt;source&lt;/code&gt;, &lt;code&gt;destination&lt;/code&gt; 取代 &lt;code&gt;a1&lt;/code&gt;, &lt;code&gt;a2&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;同一概念統一字詞&lt;/li&gt;
&lt;li&gt;在命名內加入 context，或是&lt;a href="https://hackmd.io/@dwy6626/clean-code-class-context"&gt;用 Class 包起來&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="善用詞性"&gt;善用詞性&lt;/h3&gt;
&lt;p&gt;類別：&lt;strong&gt;名詞&lt;/strong&gt;&lt;br&gt;
方法：&lt;strong&gt;動詞&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="函式"&gt;函式&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;多型 &amp;gt; switch case&lt;/li&gt;
&lt;li&gt;參數越少越好
&lt;ul&gt;
&lt;li&gt;可以把參數包在類別裡，例如 &lt;code&gt;point&lt;/code&gt; 取代 &lt;code&gt;x, y&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;No side effect
&lt;ul&gt;
&lt;li&gt;不要做名字以外的事情&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;讓物件改變自己，而不是讓函式輸入物件&lt;/li&gt;
&lt;li&gt;查改分離（Commend / Query 分離）&lt;/li&gt;
&lt;li&gt;用&lt;strong&gt;例外處理&lt;/strong&gt;取代 if 回傳錯誤碼
&lt;ul&gt;
&lt;li&gt;try&amp;hellip;finally block 應該&lt;strong&gt;獨佔&lt;/strong&gt;函式（就是一件事）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="single-responsibility-principle-srp-單一職責原則"&gt;Single Responsibility Principle, SRP 單一職責原則&lt;/h3&gt;
&lt;p&gt;一個類別或一個模組，應該只有一個讓你去修改他的理由（一種職責）&lt;/p&gt;
&lt;p&gt;要能有簡潔的命名，否則就該拆開成兩個類別 / 兩個模組&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;只有一個層次的抽象概念&lt;/li&gt;
&lt;li&gt;不要傳 flag（true/false）進函數讓他做兩件事&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="open-closed-principle-ocp-開放閉合原則"&gt;Open Closed Principle, OCP 開放閉合原則&lt;/h3&gt;
&lt;p&gt;要設計得易於延展（例如：繼承），不用因新型態、新需求加入而改變原本的部分&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;對擴展開放，對修改封閉&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="註解"&gt;註解&lt;/h2&gt;
&lt;p&gt;彌補程式碼&lt;strong&gt;表達意圖的失敗&lt;/strong&gt;&lt;br&gt;
註解容易隨著時間失真，常常會沒有跟著程式碼一起改到（&lt;strong&gt;只有程式碼是真的&lt;/strong&gt;）&lt;/p&gt;
&lt;p&gt;-&amp;gt; 透過修改程式碼來移除註解&lt;/p&gt;
&lt;p&gt;使用 git 等版本控制系統後，就不需要的註解：版本日誌 / 程式碼修改 / 作者等資訊&lt;/p&gt;
&lt;h3 id="註解應該最少只留必要的註解"&gt;註解應該最少，只留必要的註解&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;法律型註解&lt;/li&gt;
&lt;li&gt;資訊型註解
&lt;ul&gt;
&lt;li&gt;function doc（產生文件的註解）&lt;/li&gt;
&lt;li&gt;formatter 會挑出的格式&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;意圖的解釋
&lt;ul&gt;
&lt;li&gt;一段複雜 / magic code 是為了解決什麼&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;後果告誡
&lt;ul&gt;
&lt;li&gt;e.g. 為什麼不跑這個測試&lt;/li&gt;
&lt;li&gt;thread unsafety&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;TODO&lt;/li&gt;
&lt;li&gt;放大重要性：&amp;quot;&lt;strong&gt;下面這行很重要，因為&amp;hellip;&lt;/strong&gt;&amp;quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="編排"&gt;編排&lt;/h2&gt;
&lt;p&gt;寫 code，像編排一頁報紙&lt;/p&gt;
&lt;h3 id="vertical"&gt;Vertical&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;空行&lt;/strong&gt;分隔：package / class / function&lt;/li&gt;
&lt;li&gt;關聯的 code 要連續（script / function &amp;hellip;）
&lt;ul&gt;
&lt;li&gt;互相呼叫 / 引用&lt;/li&gt;
&lt;li&gt;Concept 相近&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;local variable 宣告盡可能靠近使用的地方&lt;/li&gt;
&lt;li&gt;Instance variable 宣告在最上方 (也可以依照 C++ 的 Scissors rule 放在最下方)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="horizontal"&gt;Horizontal&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;寬度 ~100-120 以下，以不要捲動畫面為主&lt;/li&gt;
&lt;li&gt;assignment 兩端空白
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;user_name = 'david'&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;用空白來強調運算子的優先序
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;return b*b - 4*a*c&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不要&lt;/strong&gt;水平對齊，會需要水平對齊代表列表太長&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="物件與資料結構"&gt;物件與資料結構&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;OOP 容易添加新的類別（duck-typing）&lt;/li&gt;
&lt;li&gt;Struct 容易添加新的 function&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="the-law-of-demeter--principle-of-least-knowledge"&gt;The Law of Demeter / Principle of Least Knowledge&lt;/h3&gt;
&lt;p&gt;Loose coupling&lt;/p&gt;
&lt;p&gt;Abstraction: 模組&lt;strong&gt;不該&lt;/strong&gt;知道它所操作的物件的&lt;strong&gt;內部運作&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="火車事故-train-wreck"&gt;火車事故 (train wreck)&lt;/h3&gt;
&lt;p&gt;一連串的連續呼叫，對於物件 &lt;code&gt;get_options&lt;/code&gt; 內部理解太深&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;output_dir &lt;span style="color:#f92672"&gt;=&lt;/span&gt; obj&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get_options()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get_scratch_dir()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get_absolute_path()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Clean Code 認為違反了 Law of Demeter，以下列方式分割：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;opt &lt;span style="color:#f92672"&gt;=&lt;/span&gt; obj&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get_options()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;scratch_dir &lt;span style="color:#f92672"&gt;=&lt;/span&gt; opt&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get_scratch_dir()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;output_dir &lt;span style="color:#f92672"&gt;=&lt;/span&gt; scratch_dir&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get_absolute_path()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="資料傳輸物件-data-transfer-objects-dto"&gt;資料傳輸物件 Data Transfer Objects (DTO)&lt;/h3&gt;
&lt;p&gt;只有 public variable&lt;br&gt;
沒有 method&lt;/p&gt;
&lt;p&gt;e.g. active record&lt;/p&gt;
&lt;p&gt;作者還批評了一般 Model 寫法，說應該要把 Bussiness rule 和 DTO 分開 XD&lt;/p&gt;
&lt;h2 id="錯誤處理"&gt;錯誤處理&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;提供 error message&lt;/li&gt;
&lt;li&gt;從 caller 的角度，定義 Exception 的類別&lt;/li&gt;
&lt;li&gt;利用特殊類別配合 duck typing 來處理特殊情況，不用總是用 &lt;code&gt;try&lt;/code&gt;&amp;hellip;&lt;code&gt;catch&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="return-null"&gt;Return Null?&lt;/h3&gt;
&lt;p&gt;盡量避免，改成：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;empty array for iteration&lt;/li&gt;
&lt;li&gt;在函式內部做 assertion 來避免會造成 null 的結果&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="interface"&gt;Interface&lt;/h2&gt;
&lt;p&gt;你的 code 和 3rd-party 的 code 的 interface&lt;/p&gt;
&lt;p&gt;最小化會依賴於 3rd-party 的部分&lt;/p&gt;
&lt;h3 id="adapter"&gt;Adapter&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;把 3rd-party 封裝起來，不要直接在 code 內傳遞他&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="unit-test"&gt;Unit Test&lt;/h2&gt;
&lt;p&gt;測試也要好維護&lt;/p&gt;
&lt;p&gt;讓你的測試變得很好閱讀：封裝細節&lt;/p&gt;
&lt;h3 id="test-driven-delopment"&gt;Test-Driven Delopment&lt;/h3&gt;
&lt;p&gt;三大法則&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;寫測試 -&amp;gt; 寫程式&lt;/li&gt;
&lt;li&gt;只寫剛好無法通過的測試&lt;/li&gt;
&lt;li&gt;只寫剛好能透過當前測試的程式&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="styling"&gt;Styling&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Given-When-Then&lt;/li&gt;
&lt;li&gt;One Assert per example (原則上)&lt;/li&gt;
&lt;li&gt;One concept per example&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="first"&gt;F.I.R.S.T.&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Fast: 測試程式不能跑太慢，不然你會不想跑 XD&lt;/li&gt;
&lt;li&gt;Independent: 區塊間不應該互相依賴，可以獨立跑某些測試，而且不能因為一個 fail 導致後續的 fail&lt;/li&gt;
&lt;li&gt;Repeatable: 在 Test / Development / Production 都能跑&lt;/li&gt;
&lt;li&gt;Self-validating: 通過 / 失敗 要明確，不要把重要結果放 log 裡面&lt;/li&gt;
&lt;li&gt;Timely: 即時寫測試，不要拖到寫完功能之後&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="class"&gt;Class&lt;/h2&gt;
&lt;h3 id="封裝"&gt;封裝&lt;/h3&gt;
&lt;p&gt;Keep private 除非需要開放給測試或其他 module&lt;/p&gt;
&lt;h3 id="凝聚性"&gt;凝聚性&lt;/h3&gt;
&lt;p&gt;盡量減少你的 instance variables&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;當一群 Instance variables 在一些 methods 都需要用到時，也許他們該被拆出去變成一個新類別&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="dependency-inversion-principle-dip"&gt;Dependency Inversion Principle (DIP)&lt;/h3&gt;
&lt;p&gt;類別要 depends on 抽象概念，不要 depends on 具體細節&lt;/p&gt;
&lt;h2 id="system"&gt;System&lt;/h2&gt;
&lt;p&gt;將所有&lt;strong&gt;關注的事&lt;/strong&gt;分開&lt;/p&gt;
&lt;p&gt;每個關注領域分開成不同模組，並以 interface 整合&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lazy initialization 同時進行了「initialize」和「執行」，少用&lt;/li&gt;
&lt;li&gt;使用一個主程式 Main 來建造物件，並讓物件各司其職&lt;/li&gt;
&lt;li&gt;使用 Dependency Injection 來減少元件之間的 Coupling&lt;/li&gt;
&lt;li&gt;善用 DSL (Domain-Specific Language) 來增加可讀性&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;註：這張多講了很多專有名詞，所以我是跳著看的（畢竟我跟 Java 不熟）。&lt;/p&gt;
&lt;h2 id="羽化-emergence"&gt;羽化 Emergence&lt;/h2&gt;
&lt;p&gt;其實就是個小結。&lt;/p&gt;
&lt;h3 id="四個簡單準則"&gt;四個簡單準則&lt;/h3&gt;
&lt;p&gt;重要性由上而下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;執行完所有測試&lt;/li&gt;
&lt;li&gt;沒有重複的部分&lt;/li&gt;
&lt;li&gt;表達 Programmer 的本意&lt;/li&gt;
&lt;li&gt;最少的類別與方法&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="執行完所有測試"&gt;執行完所有測試&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;能夠撰寫測試，類別自然較為小型且單一用途&lt;/li&gt;
&lt;li&gt;方便重構&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="平行化"&gt;平行化&lt;/h2&gt;
&lt;p&gt;將「做什麼」和「何時做」分離，讓你的 code 可以隨插即用 &lt;del&gt;（跟寫輪眼一樣）&lt;/del&gt;&lt;/p&gt;
&lt;h3 id="一些聲明"&gt;一些聲明&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;平行化會帶來額外負擔&lt;/li&gt;
&lt;li&gt;正確的平行化是複雜的，即使原本的問題很簡單&lt;/li&gt;
&lt;li&gt;平行化程式的錯誤不容易重複出現，容易被忽略&lt;/li&gt;
&lt;li&gt;常常需要根本性的修改&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="能夠幫助平行化的原則"&gt;能夠幫助平行化的原則&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;SRP 單一職責原則&lt;/li&gt;
&lt;li&gt;限制資料的視野&lt;/li&gt;
&lt;li&gt;使用資料的 copy 而不要直接共享&lt;/li&gt;
&lt;li&gt;讓 thread 盡可能獨立運行&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;當然還有了解當下語言的相關函式庫，撰寫各種順序的測試，讀一下作業系統相關章節等等&lt;/p&gt;
&lt;h2 id="最後的清單程式碼的氣味"&gt;最後的清單：程式碼的氣味&lt;/h2&gt;
&lt;p&gt;一些會讓人修改 code 的原因，只寫下我覺得前面沒記錄到的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;多步驟才能 Build / Test&lt;/li&gt;
&lt;li&gt;一個檔案多種語言（例如說不要再 &lt;code&gt;.py&lt;/code&gt; 裡面硬寫 HTML）&lt;/li&gt;
&lt;li&gt;Boundary Condition 壞掉（想起那個二分搜尋法&amp;hellip;）
&lt;ul&gt;
&lt;li&gt;封裝他們&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;無視安全規範（例如：關掉失敗的測試、警告等）&lt;/li&gt;
&lt;li&gt;Magic Number: 出現在 Code 裡面的意義不明的數字（給個名字吧）&lt;/li&gt;
&lt;li&gt;Configuration 要放在高的抽象層&lt;/li&gt;
&lt;li&gt;Scope 大，變數名稱拉長&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="延伸閱讀"&gt;延伸閱讀&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/ryanmcdermott/clean-code-javascript"&gt;Clean Code JavaScript&lt;/a&gt;，如果是寫 JS 的很推薦閱讀這個 repo&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/zedr/clean-code-python"&gt;Clean Code Python&lt;/a&gt;，Python 版本的 repo，受到上面那個 repo 啟發的&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>用 zsh + zim + powerlevel10k 讓你的 Terminal 潮又快</title><link>https://dwye.dev/post/zsh-zim-powerlevel10k/</link><pubDate>Mon, 07 Sep 2020 11:41:25 +0800</pubDate><guid>https://dwye.dev/post/zsh-zim-powerlevel10k/</guid><description>
&lt;p&gt;在開始之前，還是要先知道為什麼需要這些工具，就先上圖吧：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://i.imgur.com/YWNPQiG.png" alt="zsh &amp;#43; zim &amp;#43; powerlevel10k demo"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在 Terminal 內顯示你想要的資訊：git / 時間 / 自訂路徑 / conda / rvm / nvm / 執行結果顯示等等，應有盡有。&lt;/li&gt;
&lt;li&gt;更方便的自動補完，可以直接用 tab 或方向鍵選擇想要補完的結果。&lt;/li&gt;
&lt;li&gt;可以輸入部分指令，就顯示之前打過的指令，也可以用方向鍵上下搜尋&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;hellip;&amp;hellip;&lt;/p&gt;
&lt;p&gt;可能還有更多我還沒挖掘出來的方便性，但光是這三點，就足以讓我在每個工作環境都裝上 zsh + zim + powerlevel10k 了。&lt;/p&gt;
&lt;p&gt;寫這篇的原因，是因為目前看到主要的文章都還是透過 oh-my-zsh 安裝，但其實根本就不需要 XD&lt;/p&gt;
&lt;p&gt;甚至有的還是推薦 powerlevel9k，但是根據我自己的經驗，真的&amp;hellip;很慢，體感上的慢。然而若直接繞過 oh-my-zsh 使用 powerlevel10k，會少了一些客製化的方便性，所以這邊就來推薦 Zsh IMproved FrameWork (zim)，真的不太需要再做任何修改，就能享受常用到自動補完、選擇歷史指令等等功能。&lt;/p&gt;
&lt;h2 id="安裝-zsh"&gt;安裝 zsh&lt;/h2&gt;
&lt;h3 id="masos"&gt;masOS&lt;/h3&gt;
&lt;p&gt;系統有內建了，可以跳過。&lt;/p&gt;
&lt;h3 id="ubuntu"&gt;Ubuntu&lt;/h3&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;sudo apt install zsh
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="切換預設-shell"&gt;切換預設 Shell&lt;/h2&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;chsh -s $(which zsh)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;也可以手動切過去：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;exec zsh
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="安裝-zim"&gt;安裝 zim&lt;/h2&gt;
&lt;p&gt;根據他們 GitHub 的說明，就是希望&lt;strong&gt;快&lt;/strong&gt;，而且能包含常用的補全功能等等，號稱能比其他 framework 快上一倍：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/zimfw/zimfw/wiki/Speed"&gt;https://github.com/zimfw/zimfw/wiki/Speed&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;安裝方式極其簡單，可以利用 curl 或是 wget 一行解決。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;curl -fsSL https://raw.githubusercontent.com/zimfw/install/master/install.zsh | zsh
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;另外如果有些人不喜歡常常被問要不要更新，可以透過下面的指令來取消檢查（通常不太建議&amp;hellip;畢竟每 30 天才會問一次）：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;zstyle &amp;#39;:zim&amp;#39; disable-version-check yes
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="安裝-powerlevel10k"&gt;安裝 Powerlevel10k&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/romkatv/powerlevel10k"&gt;https://github.com/romkatv/powerlevel10k&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;zsh 主題，讓你的 zsh 變得更美。&lt;/p&gt;
&lt;h3 id="在-zim-中加入設定"&gt;在 zim 中加入設定&lt;/h3&gt;
&lt;p&gt;有了 zim 之後，就直接使用 zim 內建的 &lt;code&gt;~/.zimrc&lt;/code&gt; 來安裝吧。首先在 &lt;code&gt;~/.zimrc&lt;/code&gt; 加入這行：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;zmodule romkatv/powerlevel10k
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果你還沒有一個好的文字編輯器（但是建議你去找一個&amp;hellip;不想用純文字介面的可以使用 &lt;a href="https://code.visualstudio.com/docs/setup/mac"&gt;VSCode 的 Shell command&lt;/a&gt;），也可以用懶人方式，直接用 echo 指令把文字加在檔案後面：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;echo &amp;#34;zmodule romkatv/powerlevel10k&amp;#34; &amp;gt;&amp;gt; ~/.zimrc
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="接著安裝它"&gt;接著安裝它&lt;/h3&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;zimfw install
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;然後就可以直接輸入 &lt;code&gt;exec zsh&lt;/code&gt; 切換過去測試了！第一次會有一些步驟要設定，當然也能直接跳過，之後自己在 &lt;code&gt;.zshrc&lt;/code&gt; 改就好。&lt;/p&gt;
&lt;p&gt;如果不小心錯過了起始的設定小程式，也可以透過 &lt;code&gt;p10k configure&lt;/code&gt; 重跑一次設定。&lt;/p&gt;
&lt;h2 id="安裝特殊字體-nerd-font"&gt;安裝特殊字體 Nerd Font&lt;/h2&gt;
&lt;p&gt;其實就是一般的字體，只是加上了些特殊符號當作 Icons。如果沒有安裝，可能會看到有些東西是亂碼，那些就是 Icons，而我們原本的字體並沒有包含這些 Icons 所以會顯示亂碼。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;p10k configure&lt;/code&gt; 的第一步就可以選擇是否直接安裝字體，很方便。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://i.imgur.com/OfNhvjE.png" alt="p10k configure install font?"&gt;&lt;/p&gt;
&lt;h3 id="自己安裝字體"&gt;自己安裝字體&lt;/h3&gt;
&lt;p&gt;如果想要使用其他的 Nerd font 可以到 &lt;a href="https://nerdfonts.com/"&gt;https://nerdfonts.com/&lt;/a&gt; 裡面的 Downloads 分頁找，有蠻多字體可以選擇。&lt;/p&gt;
&lt;p&gt;我自己是 patch 內建的 &lt;code&gt;Monaco&lt;/code&gt; 字體，步驟稍微複雜，可能需要另外拆開一篇分享。所以這邊就先從裡面挑一個吧，powerlevel10k 官方推薦的字體是 &lt;code&gt;Meslo Nerd Font&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;安裝完之後幫 Terminal 套上字型設定，就大功告成了。&lt;/p&gt;
&lt;p&gt;macOS 可以使用 Homebrew 的 &lt;code&gt;cask&lt;/code&gt; 來安裝：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;brew tap homebrew/cask-fonts
brew cask install font-meslo-lg-nerd-font
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果你碰到舊版的 &lt;code&gt;caskroom/fonts&lt;/code&gt;，也許會在安裝時碰上衝突：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Error: Cask font-meslo-lg-nerd-font exists in multiple taps:
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;這時候把舊的 tap 刪掉就好了（關於 tap 可以參考:&lt;a href="https://docs.brew.sh/Taps"&gt;Taps (Third-Party Repositories)&lt;/a&gt;）&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;brew untap caskroom/fonts
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;能用 &lt;code&gt;brew cask&lt;/code&gt; 安裝的完整清單可以上他們的 GitHub 找：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Homebrew/homebrew-cask-fonts/tree/master/Casks"&gt;https://github.com/Homebrew/homebrew-cask-fonts/tree/master/Casks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;安裝完之後到 iTerm2 裡面套用：&lt;/p&gt;
&lt;p&gt;選單 &amp;gt; Preferences &amp;gt; Profiles &amp;gt; Text 找到 Font 設定：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://i.imgur.com/K67dFs3.png" alt="iTerm2 字型設定"&gt;&lt;/p&gt;
&lt;h2 id="小結"&gt;小結&lt;/h2&gt;
&lt;p&gt;到這邊其實只是初始設定完成，其實後面還有很多個人化設定可以研究，不過其實原始的預設值就很夠用了！&lt;/p&gt;
&lt;p&gt;因為我是從 powerlevel9k 來的使用者，所以我自己還習慣使用 powerlevel9k 的設定如下：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# ~/.zshrc
POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(dir vcs) # &amp;lt;= left prompt
POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=(status anaconda) # &amp;lt;= right prompt
POWERLEVEL9K_ANACONDA_LEFT_DELIMITER=
POWERLEVEL9K_ANACONDA_RIGHT_DELIMITER=
POWERLEVEL9K_SHORTEN_DELIMITER=&amp;#34;.&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(是說今天才看到 powerlevel9k 已經停止維護了 XD)&lt;/p&gt;
&lt;p&gt;當然如果你是新使用者，建議還是直接修改他們的專用設定檔 &lt;code&gt;~/.p10k.zsh&lt;/code&gt;&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>為什麼有些人離開 Notion 了</title><link>https://dwye.dev/post/notion-privacy-concerns/</link><pubDate>Sun, 09 Aug 2020 00:41:23 +0800</pubDate><guid>https://dwye.dev/post/notion-privacy-concerns/</guid><description>
&lt;ol&gt;
&lt;li&gt;偷看用戶資料&lt;/li&gt;
&lt;li&gt;中國防火長城的快速解封&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;來說說為什麼這些會被認為是有疑慮的：&lt;/p&gt;
&lt;h3 id="偷看用戶資料"&gt;偷看用戶資料&lt;/h3&gt;
&lt;p&gt;當然有問題，尤其是有人出面指控 Notion 為了商業利益，偷看了某公司資料之後，還跟公司提案合作。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://i.imgur.com/aqoZrXM.png" alt="notion privacy discussion"&gt;&lt;/p&gt;
&lt;p&gt;圖片截圖自 &lt;a href="https://www.facebook.com/groups/notion.so.taiwan/permalink/650523105498836/"&gt;Notion Taiwan FB 社團的公開文章&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;還有人指出 &lt;a href="https://www.notion.so/3468d120cf614d4c9014c09f6adc9091"&gt;Notion 隱私政策&lt;/a&gt;本來就允許他們偷看你的內容，只要能和他們商業利益有關：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Business Development and Strategic Partnerships: We may collect personal information from individuals and third parties to assess and pursue potential business opportunities.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="中國防火長城的快速解封"&gt;中國防火長城的快速解封&lt;/h3&gt;
&lt;p&gt;被中國防火長城擋起來的服務非常多，然而 Notion 在被封&lt;strong&gt;不到一個月&lt;/strong&gt;就解封了。就開始有人懷疑，是不是私下和中國達成什麼交易。&lt;/p&gt;
&lt;p&gt;俗話說，&lt;strong&gt;便宜的最貴&lt;/strong&gt;，&lt;strong&gt;資料就是金錢&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="notion-官方的回應"&gt;Notion 官方的回應&lt;/h2&gt;
&lt;p&gt;基於這把火在台灣燒得蠻旺的，所以有人就去和 Notion 公司聯絡，得到了官方的回應：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.notion.so/Notion-1c9a7142157147b484bc381c3e3b35d9"&gt;Notion 官方聲明稿&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;關於隱私權內商業策略的條款也已經刪除了。不過那個&lt;a href="https://www.notion.so/Personal-Use-Terms-of-Service-00e4e5d0f2b9411cbee6493f15779500"&gt;Terms of Service&lt;/a&gt;裡面的資料收集部分還是寫的很精彩，在意的話不妨去看看。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Notion Content &amp;amp; Use Policy and Monitoring Content: You agree that your use and all User Content will comply with &lt;a href="https://www.notion.so/Content-Use-Policy-1b9a773d5583486cb5c1d39a8d777a55"&gt;Notion’s Content &amp;amp; Use Policy&lt;/a&gt;. Although we’re not obligated to monitor access to or use of the Service or Content or to review or edit any Content, we have the right to do so for the purpose of operating the Service, to ensure compliance with these Terms and to comply with applicable law or other legal requirements. We reserve the right, but are not obligated, to remove or disable access to any Content, including User Content, at any time and without notice, including, but not limited to, if we, at our sole discretion, consider any Content to be objectionable or in violation of these Terms. We have the right to investigate violations of these Terms or conduct that affects the Services. We may also consult and cooperate with law enforcement authorities to prosecute users who violate the law.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;他們保留了所有能夠看使用者資料的權力，並也有把使用者資料移除或是讓你無法存取資料的權力。&lt;/p&gt;
&lt;h2 id="其他類似的服務"&gt;其他類似的服務&lt;/h2&gt;
&lt;p&gt;如果你要逃了，在相關討論串看到別人推薦的一些 Notion 的替代方案，我還沒有空研究，如果有人有心得歡迎分享：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://joplinapp.org/"&gt;Joplin&lt;/a&gt;：開源，可以自架或使用 Dropbox 同步&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nimbusweb.me/"&gt;Nimbus Note&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://coda.io/welcome"&gt;Coda&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://clickup.com/"&gt;ClickUp&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;只有在 Mac / iOS 上的話可以考慮：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://bear.app/"&gt;Bear&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;原生 Apple 備忘錄&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;當然你也可以試試原本的老品牌們&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OneNote&lt;/li&gt;
&lt;li&gt;Evernote&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你在手機上編輯的需求不大的話（像是我這種永遠戴著筆電跑來跑去的人），我也很推薦台灣出產的線上共筆軟體：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hackmd.io/"&gt;HackMD&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;它的功能開始越來越豐富了，而且有&lt;a href="https://hackmd.io/s/features-tw"&gt;完整的中文教學&lt;/a&gt;，想要用 Markdown 寫筆記的話一定要試試！&lt;/p&gt;
&lt;h2 id="我的選擇"&gt;我的選擇&lt;/h2&gt;
&lt;p&gt;因為沒有時間研究（文章產出都減量了&amp;hellip;這篇還拖搞這麼久才出），加上官方有釋出善意，所以我決定先暫時留在 Notion + HackMD 的組合。&lt;/p&gt;
&lt;p&gt;防火長城問題，本來就是中國愛怎樣就怎樣。而隱私問題，除非筆記軟體主打 E2E 加密到資料庫（進資料庫前也會加密），不然你也很難保證公司不會去看你的筆記內容。Reddit 上的討論也大多傾向把 Notion 拿來純做筆記，而不要把任何敏感資料（商業機密 / 密碼）放在上頭。我自己的也是拿來做個人筆記而已。&lt;/p&gt;
&lt;p&gt;但每個人都能有自己的選擇，如果你看到這些問題，而你認為某個服務不會有這些問題，那就跳槽吧，這樣也能鞭策 Notion 做得更好。&lt;/p&gt;
&lt;p&gt;這才是自由民主的真諦啊。&lt;/p&gt;
&lt;h2 id="參考資料"&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.facebook.com/racklin1002/posts/3345364142149274"&gt;Rack Lin 的 FB 文章&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.plurk.com/p/nud4kj"&gt;Plurk 偷偷說討論串&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>HackMD 彩蛋：讓你的文字充滿力量</title><link>https://dwye.dev/post/hackmd-konami-code/</link><pubDate>Mon, 03 Aug 2020 00:41:00 +0800</pubDate><guid>https://dwye.dev/post/hackmd-konami-code/</guid><description>
&lt;p&gt;&lt;img src="https://dwye.dev/img/HackMD-Power.gif" alt="hackmd komami code"&gt;&lt;/p&gt;
&lt;p&gt;在 HackMD 畫面中輸入知名的 &lt;a href="https://zh.wikipedia.org/wiki/%E7%A7%91%E4%B9%90%E7%BE%8E%E7%A7%98%E6%8A%80"&gt;Konami Code（科樂美秘技）&lt;/a&gt;：&lt;strong&gt;上上下下左右左右BA&lt;/strong&gt;，就會啟動 POWER MODE，讓你打字的時候充滿力量！！&lt;/p&gt;
&lt;h3 id="關於-power-mode"&gt;關於 Power Mode&lt;/h3&gt;
&lt;p&gt;&lt;del&gt;在輸入文字的時候感受到力量注入編輯器&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;這個 Power Mode 其實不是 HackMD 原創，其實很多 IDE 或是其他文字輸入的地方都有類似的功能，可以參考這個列表：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/codeinthedark/awesome-power-mode"&gt;https://github.com/codeinthedark/awesome-power-mode&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例如說我拿來寫這篇文章的 VSCode 也有 Power Mode 的 Extension（外掛）可以使用：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=hoovercj.vscode-power-mode"&gt;https://marketplace.visualstudio.com/items?itemName=hoovercj.vscode-power-mode&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;寫扣到厭煩的時候不妨裝來玩看看吧 XD&lt;/p&gt;
&lt;h3 id="如果想關掉-power-mode-怎麼辦"&gt;如果想關掉 Power Mode 怎麼辦&lt;/h3&gt;
&lt;p&gt;我試過再輸入一次&lt;strong&gt;上上下下左右左右BA&lt;/strong&gt;，結果沒有發生任何事。&lt;/p&gt;
&lt;p&gt;還好官方雖然愛玩梗但是也有提供解答：&lt;br&gt;
「&lt;a href="https://hackmd.io/c/tutorials-tw/%2F%40docs%2Fdisable-activate-power-mode-tw"&gt;如何關閉 Activate Power Mode&lt;/a&gt;」這篇寫到，其實可以在下方設定直接把勾勾取消掉就好了。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://i.imgur.com/XksMXrC.png" alt="Disable Power Mode HackMD"&gt;&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>Matplotlib 中文字體亂碼問題</title><link>https://dwye.dev/post/matplotlib-font/</link><pubDate>Tue, 03 Mar 2020 23:41:25 +0800</pubDate><guid>https://dwye.dev/post/matplotlib-font/</guid><description>
&lt;p&gt;花了一段時間解決這個問題，發篇文章記錄一下。&lt;/p&gt;
&lt;h2 id="嘗試"&gt;嘗試&lt;/h2&gt;
&lt;p&gt;一開始想到的是字型設定，所以就看一下系統有哪些中文字型：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;fc-list :lang&lt;span style="color:#f92672"&gt;=&lt;/span&gt;zh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;挑一個字體在畫圖前修改 font family：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-Python" data-lang="Python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;plt&lt;span style="color:#f92672"&gt;.&lt;/span&gt;rcParams[&lt;span style="color:#e6db74"&gt;&amp;#39;font.family&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;Noto Sans CJK TC&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然後就炸了&amp;hellip;噴出錯誤訊息：&lt;br&gt;
&lt;code&gt;/usr/local/lib/python3.7/site-packages/matplotlib/font_manager.py:1241: UserWarning: findfont: Font family ['Noto Sans CJK TC'] not found. Falling back to DejaVu Sans.&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;後來找到「&lt;a href="https://medium.com/marketingdatascience/f7b3773a889b"&gt;解決 Python 3 Matplotlib 與 Seaborn 視覺化套件中文顯示問題&lt;/a&gt;」這篇文章，裡面提供兩種方法，一種是直接套絕對路徑，另一種則是手動去改設定檔，兩個方法對我而言都蠻麻煩的。&lt;/p&gt;
&lt;h2 id="解法"&gt;解法&lt;/h2&gt;
&lt;p&gt;想說能不能動到最少的 code 來解決這個問題，於是換個想法，可能是 &lt;code&gt;Matplotlib&lt;/code&gt; 沒有找到 &lt;code&gt;fc-list&lt;/code&gt; 列出來的字體，所以就來查到底哪些字體是 &lt;code&gt;Matplotlib&lt;/code&gt; 吃得到的呢？&lt;/p&gt;
&lt;p&gt;於是就找到這篇 stackoverflow：「&lt;a href="https://stackoverflow.com/questions/18821795/"&gt;How can i get list of font family (or Name of Font) in matplotlib&lt;/a&gt;」&lt;br&gt;
底下的人回答用這個方式可以列出 &lt;code&gt;Matplotlib&lt;/code&gt; 找得到的 &lt;code&gt;.ttf&lt;/code&gt; 字體：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-Python" data-lang="Python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; matplotlib.font_manager
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[f&lt;span style="color:#f92672"&gt;.&lt;/span&gt;name &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; f &lt;span style="color:#f92672"&gt;in&lt;/span&gt; matplotlib&lt;span style="color:#f92672"&gt;.&lt;/span&gt;font_manager&lt;span style="color:#f92672"&gt;.&lt;/span&gt;fontManager&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ttflist]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;不過列出來很大一串，所以稍微修改一下，用集合去掉重複的：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-Python" data-lang="Python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; matplotlib &lt;span style="color:#f92672"&gt;import&lt;/span&gt; font_manager
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;font_set &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {f&lt;span style="color:#f92672"&gt;.&lt;/span&gt;name &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; f &lt;span style="color:#f92672"&gt;in&lt;/span&gt; font_manager&lt;span style="color:#f92672"&gt;.&lt;/span&gt;fontManager&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ttflist}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; f &lt;span style="color:#f92672"&gt;in&lt;/span&gt; font_set:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(f)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;就可以找到所有可用的 font family 了，再也不會被 Falling back to DejaVu Sans 了。&lt;/p&gt;
&lt;p&gt;最後找到的是「AR PL UMing CN」這個字體，居然是用 CN 結尾，而沒有 TW 的，真是神奇。但是至少可以用了。&lt;/p&gt;
&lt;p&gt;順便提供一段簡單的測試代碼：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-Python" data-lang="Python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; matplotlib &lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyplot &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; plt
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;plt&lt;span style="color:#f92672"&gt;.&lt;/span&gt;rcParams[&lt;span style="color:#e6db74"&gt;&amp;#39;font.family&amp;#39;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;AR PL UMing CN&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;plt&lt;span style="color:#f92672"&gt;.&lt;/span&gt;text(&lt;span style="color:#ae81ff"&gt;.5&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;.5&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;中文&amp;#39;&lt;/span&gt;, fontsize&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;50&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;plt&lt;span style="color:#f92672"&gt;.&lt;/span&gt;show()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;下面這是我後來用 macOS 補截圖的，字型和上面不一樣，是正常的&lt;strong&gt;新細明體&lt;/strong&gt; &lt;code&gt;PMingLiU&lt;/code&gt;&lt;br&gt;
&lt;img src="https://i.imgur.com/XhFWbE9.png" alt="matplotlib chinese demo"&gt;&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>表單大戰 10 週年</title><link>https://dwye.dev/post/form-war/</link><pubDate>Fri, 14 Feb 2020 14:00:00 +0800</pubDate><guid>https://dwye.dev/post/form-war/</guid><description>
&lt;p&gt;十年前的 3/4，「&lt;strong&gt;表單大戰&lt;/strong&gt;」誕生了。&lt;/p&gt;
&lt;p&gt;於師大附中電算社，展出至少四屆成發（2010~2013）、以及三屆資訊營 Visual Basic 課程的展示作品（2010~2012）的表單大戰，今年終於十歲啦！！&lt;/p&gt;
&lt;p&gt;因此，我在此宣佈，表單大戰 10 週年計畫，正式展開！&lt;/p&gt;
&lt;p&gt;&lt;del&gt;Visual Basic 再戰十年&lt;/del&gt;&lt;/p&gt;
&lt;h2 id="主要項目如下"&gt;主要項目如下：&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;表單大戰正式開源！請參考： &lt;a href="https://github.com/dwy6626/FormWar"&gt;https://github.com/dwy6626/FormWar&lt;/a&gt;&lt;br&gt;
（若要參考 2013 成發展出的版本，可以從 tag 選 &lt;code&gt;v1.3.1.2&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;發表 &lt;code&gt;v1.4.5.3&lt;/code&gt; 版更新：&lt;br&gt;
&lt;a href="https://github.com/dwy6626/FormWar/releases/download/v1.4.5.3/FormWar.exe"&gt;下載點&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Code 大幅簡化（減少 copy paste）&lt;/li&gt;
&lt;li&gt;更厲害的表單分身&lt;/li&gt;
&lt;li&gt;更多移動方式&lt;/li&gt;
&lt;li&gt;更多的攻擊方式！&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="所以關於表單大戰"&gt;所以關於表單大戰&lt;/h2&gt;
&lt;p&gt;我於 2010 年在師大附中電算社用 VB 實作的小程式，而他是一個以 Visual Basic 2008 （VB）寫成的小遊戲（也因為是 VB 所以是 Windows only）。&lt;/p&gt;
&lt;p&gt;簡單來說，就是使用滑鼠追逐奔跑的 Windows Form（表單）的小遊戲。&lt;/p&gt;
&lt;p&gt;表單，其實就是 Windows Form 的 Form 的中文直譯，比較接近現在常用的「視窗」的意思。（後來 Google 表單出現之後，表單這個名詞更常被拿去當作一個需要填寫內容回答問題並送出的線上文件。但是此表單並非彼表單）&lt;/p&gt;
&lt;p&gt;「&lt;strong&gt;表單大戰&lt;/strong&gt;」可以說是我人生第一個自主 Side Project（？）。當時是師大附中電算社高一的我，在社團主要學的是 VB（畢竟當時是第一次接觸程式語言的小孩，對於能夠馬上做出 GUI 程式的比較感興趣。）也因此自己照著社團指導老師李啟龍老師寫的「Visual Basic 2008 程式設計 16 堂特訓」開始跟著範例慢慢跑。&lt;/p&gt;
&lt;p&gt;而當時書中有一個範例程式，是使用滑鼠點選表單，表單就會跑到別的位置。經過一陣腦補之後就成了現在的樣子了。&lt;/p&gt;
&lt;p&gt;還記得發布時最陽春的第一版，一點開程式就是一個 Windows Form （表單）在畫面上奔跑，玩家要用滑鼠去點表單上面的數字，每點一次數字就會 -1，當數字歸零時就結束了。而當數字變小，表單也不會就乖乖讓你點，會放出各種招式妨礙玩家。因為要用滑鼠一直追擊表單，如果沒點好就會不小心點到桌面上各種捷徑（高中生的惡趣味十足）。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/formwar.gif" alt="form war game play"&gt;&lt;/p&gt;
&lt;p&gt;後來新的版本才出現了背景計時、主選單、難度選擇等 Features，版本號也往前推進，因為熟練（？）的玩家數量增加，因此出了個超級難的「&lt;strong&gt;不可能的任務&lt;/strong&gt;」難度，連作者都花了 9 分鐘才全破，自虐意味十足。&lt;/p&gt;
&lt;p&gt;但是這樣自己創造新遊戲然後給自己玩，應該也算是我一直以來的興趣，從最初的 LF2 改版研究了好幾年，出了一版「YDW-LF2」，主打劇情模式，但是平衡炸裂，畢竟是小學生作品（也因為是太久之前，檔案備份我也不存在了，消失的童年，真可惜）。之後是紅色警戒 2 的改版，在素材網搜集素材、利用規則 INI 編寫屬性及 AI 等，然後再做來自己挑戰&amp;hellip;。也許就是這樣的成長歷程，才會有「&lt;strong&gt;表單大戰&lt;/strong&gt;」的誕生吧？&lt;/p&gt;
&lt;h3 id="icon"&gt;Icon&lt;/h3&gt;
&lt;p&gt;&lt;img src="https://i.imgur.com/SBuBWW9.png" alt="表單大戰 Icon"&gt;&lt;/p&gt;
&lt;p&gt;「&lt;strong&gt;表單大戰&lt;/strong&gt;」的 Icon 其實是由「&lt;strong&gt;Message Box 攻擊！&lt;/strong&gt;」時會出現的警示符號與錯誤符號組成，參考「&lt;strong&gt;終極動員令二：泰伯倫之日&lt;/strong&gt;」的 Icon 排列組成：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://i.imgur.com/ngAcTSl.png" alt="終極動員令二：泰伯倫之日，圖片取自網路"&gt;&lt;/p&gt;
&lt;h2 id="為什麼會有十週年計畫"&gt;為什麼會有十週年計畫&lt;/h2&gt;
&lt;p&gt;因為最近在幫實驗室寫互動實驗程式的 GUI，因此就想參考一下以前自己是怎麼寫的，把高中的舊 Code 從雲端全部挖了出來。&lt;/p&gt;
&lt;p&gt;結果 Code 一片慘不忍賭，各種複製貼上，才想到我當時根本沒有學過真正的物件導向，也沒有 Class 和 Function 的概念，原本還想說搞不好可以當自己的作品集，可是這種程度如果放上 GitHub 根本給人笑 QQ。當時也沒有用 Git 進行版控，所以就會有各種不同版本的資料夾，於是決定就趁這個機會花點時間整理一下。&lt;/p&gt;
&lt;p&gt;所有的舊 Project 中最具代表性的當然就是這篇的主角「&lt;strong&gt;表單大戰&lt;/strong&gt;」了，因此就成了我第一個開刀的目標，剛好今年又是第一版發行的十週年，因此就有了這個突發奇想的計畫。&lt;/p&gt;
&lt;p&gt;例如說這個片段，是控制表單移動的部分：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-vbnet" data-lang="vbnet"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Randomize()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;If&lt;/span&gt; Val(Label1.Text) &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; 15 &lt;span style="color:#66d9ef"&gt;Then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;If&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.X &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; 800 &lt;span style="color:#f92672"&gt;And&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.Y &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; 600 &lt;span style="color:#f92672"&gt;And&lt;/span&gt; x_add &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt; &lt;span style="color:#f92672"&gt;And&lt;/span&gt; y_add &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.SetDesktopLocation(&lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.X &lt;span style="color:#f92672"&gt;+&lt;/span&gt; Int(Rnd() &lt;span style="color:#f92672"&gt;*&lt;/span&gt; 50) &lt;span style="color:#f92672"&gt;+&lt;/span&gt; 10, &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.Y &lt;span style="color:#f92672"&gt;+&lt;/span&gt; Int(Rnd() &lt;span style="color:#f92672"&gt;*&lt;/span&gt; 50) &lt;span style="color:#f92672"&gt;+&lt;/span&gt; 10)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ElseIf&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.X &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; 50 &lt;span style="color:#f92672"&gt;And&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.Y &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; 600 &lt;span style="color:#f92672"&gt;And&lt;/span&gt; x_add &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt; &lt;span style="color:#f92672"&gt;And&lt;/span&gt; y_add &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.SetDesktopLocation(&lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.X &lt;span style="color:#f92672"&gt;-&lt;/span&gt; Int(Rnd() &lt;span style="color:#f92672"&gt;*&lt;/span&gt; 50) &lt;span style="color:#f92672"&gt;+&lt;/span&gt; 10, &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.Y &lt;span style="color:#f92672"&gt;+&lt;/span&gt; Int(Rnd() &lt;span style="color:#f92672"&gt;*&lt;/span&gt; 50) &lt;span style="color:#f92672"&gt;+&lt;/span&gt; 10)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ElseIf&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.X &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; 50 &lt;span style="color:#f92672"&gt;And&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.Y &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; 50 &lt;span style="color:#f92672"&gt;And&lt;/span&gt; x_add &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt; &lt;span style="color:#f92672"&gt;And&lt;/span&gt; y_add &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.SetDesktopLocation(&lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.X &lt;span style="color:#f92672"&gt;-&lt;/span&gt; Int(Rnd() &lt;span style="color:#f92672"&gt;*&lt;/span&gt; 50) &lt;span style="color:#f92672"&gt;+&lt;/span&gt; 10, &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.Y &lt;span style="color:#f92672"&gt;-&lt;/span&gt; Int(Rnd() &lt;span style="color:#f92672"&gt;*&lt;/span&gt; 50) &lt;span style="color:#f92672"&gt;+&lt;/span&gt; 10)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ElseIf&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.X &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; 800 &lt;span style="color:#f92672"&gt;And&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.Y &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; 50 &lt;span style="color:#f92672"&gt;And&lt;/span&gt; x_add &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt; &lt;span style="color:#f92672"&gt;And&lt;/span&gt; y_add &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.SetDesktopLocation(&lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.X &lt;span style="color:#f92672"&gt;+&lt;/span&gt; Int(Rnd() &lt;span style="color:#f92672"&gt;*&lt;/span&gt; 50) &lt;span style="color:#f92672"&gt;+&lt;/span&gt; 10, &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.Y &lt;span style="color:#f92672"&gt;-&lt;/span&gt; Int(Rnd() &lt;span style="color:#f92672"&gt;*&lt;/span&gt; 50) &lt;span style="color:#f92672"&gt;+&lt;/span&gt; 10)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;End&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;If&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;Else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;If&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.X &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; 800 &lt;span style="color:#f92672"&gt;And&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.Y &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; 600 &lt;span style="color:#f92672"&gt;And&lt;/span&gt; x_add &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt; &lt;span style="color:#f92672"&gt;And&lt;/span&gt; y_add &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.SetDesktopLocation(&lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.X &lt;span style="color:#f92672"&gt;+&lt;/span&gt; Int(Rnd() &lt;span style="color:#f92672"&gt;*&lt;/span&gt; 50), &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.Y &lt;span style="color:#f92672"&gt;+&lt;/span&gt; Int(Rnd() &lt;span style="color:#f92672"&gt;*&lt;/span&gt; 50))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ElseIf&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.X &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; 50 &lt;span style="color:#f92672"&gt;And&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.Y &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; 600 &lt;span style="color:#f92672"&gt;And&lt;/span&gt; x_add &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt; &lt;span style="color:#f92672"&gt;And&lt;/span&gt; y_add &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.SetDesktopLocation(&lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.X &lt;span style="color:#f92672"&gt;-&lt;/span&gt; Int(Rnd() &lt;span style="color:#f92672"&gt;*&lt;/span&gt; 50), &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.Y &lt;span style="color:#f92672"&gt;+&lt;/span&gt; Int(Rnd() &lt;span style="color:#f92672"&gt;*&lt;/span&gt; 50))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ElseIf&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.X &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; 50 &lt;span style="color:#f92672"&gt;And&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.Y &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; 50 &lt;span style="color:#f92672"&gt;And&lt;/span&gt; x_add &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt; &lt;span style="color:#f92672"&gt;And&lt;/span&gt; y_add &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.SetDesktopLocation(&lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.X &lt;span style="color:#f92672"&gt;-&lt;/span&gt; Int(Rnd() &lt;span style="color:#f92672"&gt;*&lt;/span&gt; 50), &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.Y &lt;span style="color:#f92672"&gt;-&lt;/span&gt; Int(Rnd() &lt;span style="color:#f92672"&gt;*&lt;/span&gt; 50))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ElseIf&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.X &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; 800 &lt;span style="color:#f92672"&gt;And&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.Y &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; 50 &lt;span style="color:#f92672"&gt;And&lt;/span&gt; x_add &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt; &lt;span style="color:#f92672"&gt;And&lt;/span&gt; y_add &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.SetDesktopLocation(&lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.X &lt;span style="color:#f92672"&gt;+&lt;/span&gt; Int(Rnd() &lt;span style="color:#f92672"&gt;*&lt;/span&gt; 50), &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.Y &lt;span style="color:#f92672"&gt;-&lt;/span&gt; Int(Rnd() &lt;span style="color:#f92672"&gt;*&lt;/span&gt; 50))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;End&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;If&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;End&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;If&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;If&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.X &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; 800 &lt;span style="color:#f92672"&gt;And&lt;/span&gt; x_add &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; x_add &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;ElseIf&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.Y &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; 600 &lt;span style="color:#f92672"&gt;And&lt;/span&gt; y_add &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; y_add &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;ElseIf&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.Y &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; 50 &lt;span style="color:#f92672"&gt;And&lt;/span&gt; y_add &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; y_add &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;ElseIf&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Location.X &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; 50 &lt;span style="color:#f92672"&gt;And&lt;/span&gt; x_add &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; x_add &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;End&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;If&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;就是相當冗長又充滿複製貼上的代碼，而且每個分身表單都有這麼一段。因此這次的一項工程，就是把分身表單的 Code 集中起來，每次召喚分身都從同一個物件 New 一個新的出來而不是直接寫很多份 Code。&lt;/p&gt;
&lt;p&gt;而經過一些簡單的修改之後，這段 Code 變成：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-vbnet" data-lang="vbnet"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;Public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Sub&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;FormMove&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;ByRef&lt;/span&gt; form)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Randomize()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; form.SetDesktopLocation(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; form.Location.X &lt;span style="color:#f92672"&gt;+&lt;/span&gt; form.x_sign &lt;span style="color:#f92672"&gt;*&lt;/span&gt; SpeedScale,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; form.Location.Y &lt;span style="color:#f92672"&gt;+&lt;/span&gt; form.y_sign &lt;span style="color:#f92672"&gt;*&lt;/span&gt; SpeedScale
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;If&lt;/span&gt; form.Location.X &lt;span style="color:#f92672"&gt;+&lt;/span&gt; form.Size.Width &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; screenw &lt;span style="color:#f92672"&gt;And&lt;/span&gt; form.x_sign &lt;span style="color:#f92672"&gt;=&lt;/span&gt; 1 &lt;span style="color:#66d9ef"&gt;Then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; form.x_sign &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;End&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;If&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;If&lt;/span&gt; form.Location.Y &lt;span style="color:#f92672"&gt;+&lt;/span&gt; form.Size.Height &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; screenh &lt;span style="color:#f92672"&gt;And&lt;/span&gt; form.y_sign &lt;span style="color:#f92672"&gt;=&lt;/span&gt; 1 &lt;span style="color:#66d9ef"&gt;Then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; form.y_sign &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;End&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;If&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;If&lt;/span&gt; form.Location.Y &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; 0 &lt;span style="color:#f92672"&gt;And&lt;/span&gt; form.y_sign &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;1 &lt;span style="color:#66d9ef"&gt;Then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; form.y_sign &lt;span style="color:#f92672"&gt;=&lt;/span&gt; 1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;End&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;If&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;If&lt;/span&gt; form.Location.X &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; 0 &lt;span style="color:#f92672"&gt;And&lt;/span&gt; form.x_sign &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;1 &lt;span style="color:#66d9ef"&gt;Then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; form.x_sign &lt;span style="color:#f92672"&gt;=&lt;/span&gt; 1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;End&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;If&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;End&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Sub&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然後讓分身與本尊都來調用這個 Function：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-vbnet" data-lang="vbnet"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;FormMove(&lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;原本的數字 &lt;code&gt;15&lt;/code&gt; 以下會加速的設定，就可以改成另外去修改 &lt;code&gt;SpeedScale&lt;/code&gt; 這個變數：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-vbnet" data-lang="vbnet"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;If&lt;/span&gt; Val(Label1.Text) &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; levelSpeedUp &lt;span style="color:#66d9ef"&gt;Then&lt;/span&gt; &lt;span style="color:#75715e"&gt;&amp;#39;這邊設定成 15
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; SpeedScale &lt;span style="color:#f92672"&gt;*=&lt;/span&gt; 1.5
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;End&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;If&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;維護起來容易很多。（雖然我以後也不一定會繼續來維護了&amp;hellip;XD）&lt;/p&gt;
&lt;p&gt;其實有更好的寫法，利用繼承的方式共同繼承自一個基礎的表單類別，然後把 &lt;code&gt;FormMove&lt;/code&gt; 寫成 Method，不過因為工程浩大，&lt;del&gt;錢不夠多（誤）&lt;/del&gt;，因此就沒有這樣改了。&lt;/p&gt;
&lt;h3 id="小結"&gt;小結&lt;/h3&gt;
&lt;p&gt;結論就是這是一個作者自嗨的 Side Project 以及自嗨的十週年計畫，希望大家能夠真的因為這個小遊戲得到一點娛樂，也希望沒有螢幕鍵盤滑鼠因為這個程式受到傷害（？）。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://i.imgur.com/WDxYswo.png" alt="FormWar Playing"&gt;&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>Notion 小技巧</title><link>https://dwye.dev/post/notion-tricks/</link><pubDate>Thu, 30 Jan 2020 19:11:23 +0800</pubDate><guid>https://dwye.dev/post/notion-tricks/</guid><description>
&lt;p&gt;這裡搜集了一些我會用到的 Notion 的小技巧。&lt;/p&gt;
&lt;h2 id="換行"&gt;換行&lt;/h2&gt;
&lt;p&gt;跟 FB 對話框一樣，如果你想要換行，但不想產生新的 Block，&lt;br&gt;
就按 &lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;enter&lt;/code&gt;，就會在同一個 Block 內換行，可以發現這行和上行距離比較小。&lt;/p&gt;
&lt;p&gt;而且不用像原生 Markdown 一樣要硬要加兩個空白在後面（十分違反直覺），很方便。&lt;/p&gt;
&lt;h2 id="link-a-page-和-mention-a-page-的差別"&gt;Link a page 和 mention a page 的差別&lt;/h2&gt;
&lt;p&gt;Linked page 會被視為 subpage，會出現在 sidebar，所以一個 page 內不能多次重複 link 同一個 page。&lt;/p&gt;
&lt;p&gt;但是 mention a page 不會將其視為 subpage，所以可以一直提到那個 page。&lt;/p&gt;
&lt;p&gt;&lt;del&gt;因為很重要所以可以 mention 很多次。&lt;/del&gt;&lt;/p&gt;
&lt;blockquote class="twitter-tweet"&gt;&lt;p lang="en" dir="ltr"&gt;A linked page will be shown as a subpage, a mentioned page will not. For this reason, you can &lt;a href="https://twitter.com/Mention?ref_src=twsrc%5Etfw"&gt;@mention&lt;/a&gt; the same page multiple times on a single page, but you can only include one linked page. Does that make sense?&lt;/p&gt;&amp;mdash; Notion (@NotionHQ) &lt;a href="https://twitter.com/NotionHQ/status/1103489598758350848?ref_src=twsrc%5Etfw"&gt;March 7, 2019&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;
&lt;h2 id="全域搜尋"&gt;全域搜尋&lt;/h2&gt;
&lt;p&gt;Quick Find 在頁面右上角，很遠對吧，又不能用 &lt;code&gt;cmd&lt;/code&gt; + &lt;code&gt;f&lt;/code&gt;（會叫出文章內搜尋），沒關係，可以用 &lt;code&gt;cmd&lt;/code&gt; + &lt;code&gt;p&lt;/code&gt; 來快速使用全域搜尋（老實說有點難記 XD）&lt;br&gt;
（因為我的電腦是 mac，在 windows 系統的話，應該是 &lt;code&gt;ctrl&lt;/code&gt; + &lt;code&gt;p&lt;/code&gt;）&lt;/p&gt;
&lt;h2 id="編輯頁面的熱鍵組合"&gt;編輯頁面的熱鍵組合&lt;/h2&gt;
&lt;p&gt;寫上幾個比較實用的，剩下放在連結裡：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cmd&lt;/code&gt; + &lt;code&gt;option&lt;/code&gt; + &lt;code&gt;1~8&lt;/code&gt; 可以轉換 Block 格式（ &lt;code&gt;9&lt;/code&gt; 會變成新的頁面）&lt;/li&gt;
&lt;li&gt;按著 &lt;code&gt;option&lt;/code&gt; 拖拉 Block 可以直接拉出一個新的內容一樣的 Block&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cmd&lt;/code&gt; + &lt;code&gt;a&lt;/code&gt; 可以全選整個 Block 內容，如果按第二次，會全選整個頁面&lt;/li&gt;
&lt;li&gt;和其他文字編輯器一樣，可以用 &lt;code&gt;option&lt;/code&gt; 或 &lt;code&gt;cmd&lt;/code&gt; + &lt;code&gt;→&lt;/code&gt; 或 &lt;code&gt;←&lt;/code&gt; 快速移動游標&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Esc&lt;/code&gt; 可以快速選取整個 Block，如果要刪除就在選取 Block 的時候按 &lt;code&gt;Del&lt;/code&gt; 或 &lt;code&gt;Backspace&lt;/code&gt; 就好了，就不需要用到滑鼠&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;（因為我的電腦是 mac，在 windows 系統的話，應該把 &lt;code&gt;cmd&lt;/code&gt; 換成 &lt;code&gt;ctrl&lt;/code&gt; 就可以了）&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>從備忘錄搬移到 Notion</title><link>https://dwye.dev/post/apple-note-to-notion/</link><pubDate>Thu, 30 Jan 2020 17:41:24 +0800</pubDate><guid>https://dwye.dev/post/apple-note-to-notion/</guid><description>
&lt;h2 id="匯出成-md-檔"&gt;匯出成 .md 檔&lt;/h2&gt;
&lt;p&gt;首先我們需要一個小幫手：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://falcon.star-lord.me/exporter/"&gt;Notes Exporter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;這個 app 可以一鍵幫你匯出備忘錄裡面所有的筆記，UI 美美的，操作起來也方便，按下去之後等就好~~，真的很有中國人的風格 XD~~&lt;/p&gt;
&lt;p&gt;原先是在 &lt;a href="http://bear.app"&gt;Bear.app&lt;/a&gt; 的網站看到的，不過既然都是 Markdown，那我就借來用了。&lt;/p&gt;
&lt;h2 id="匯出之後"&gt;匯出之後&lt;/h2&gt;
&lt;p&gt;但總有一些不順利的，例如說匯出之後格式其實還是會跑版，但這時候已經是文字檔了，身為一個 Programmer 就算不是大神，但是寫寫腳本應該還是會的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/dwy6626/Markdown-Format-Fixer"&gt;dwy6626/Markdown-Format-Fixer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;用法很簡單：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;python main.py &lt;span style="color:#f92672"&gt;[&lt;/span&gt;your folder&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;當然前提是你要裝好 Python，&lt;del&gt;好像還有你要會用 Terminal&lt;/del&gt;，一般來說 macOS 都有內建，但我用的是 Python 3.7，注意一下版本，如果是 2.X 的話我不保證能跑。&lt;/p&gt;
&lt;p&gt;順利的話，你會拿到一堆 &lt;code&gt;.md&lt;/code&gt; 檔。&lt;/p&gt;
&lt;h2 id="匯入-notionso"&gt;匯入 Notion.so&lt;/h2&gt;
&lt;p&gt;直接利用內建 import 功能即可：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/notion-import.png" alt="notion import"&gt;&lt;/p&gt;
&lt;h2 id="還需要做的事"&gt;還需要做的事&lt;/h2&gt;
&lt;p&gt;在 Note Exporter 的 App changelog 裡面有說：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Attachments and url-links are not imported correctly.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;沒錯！這就是你需要手動修復的東西&lt;/p&gt;
&lt;p&gt;QQ&lt;/p&gt;
&lt;p&gt;加油吧&lt;/p&gt;
&lt;p&gt;我也是這樣過來的。&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>Notion 推薦文 - 強大的 Markdown 筆記整理服務</title><link>https://dwye.dev/post/notion-intro/</link><pubDate>Thu, 30 Jan 2020 00:41:23 +0800</pubDate><guid>https://dwye.dev/post/notion-intro/</guid><description>
&lt;h2 id="notion-適合誰"&gt;Notion 適合誰？&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;跟我一樣的無紙化魔人&lt;/li&gt;
&lt;li&gt;喜歡用鍵盤操作一切的人&lt;/li&gt;
&lt;li&gt;喜歡把筆記弄成看起來很潮的人&lt;/li&gt;
&lt;li&gt;Markdown 信仰者&lt;/li&gt;
&lt;li&gt;資料散佈在世界各地需要整合的人&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="我為什麼選擇-notion"&gt;我為什麼選擇 Notion&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;因為我符合上面那幾點&lt;/li&gt;
&lt;li&gt;想要跨平台（包含手機）、跨電腦（自己的、公司的或實驗室的電腦）做筆記&lt;/li&gt;
&lt;li&gt;Code Block 良好支援（不包含匯入匯出的話）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="我的筆記歷史"&gt;我的筆記歷史：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Google Drive（現在仍然部分在使用）&lt;/li&gt;
&lt;li&gt;Evernote&lt;/li&gt;
&lt;li&gt;Apple 備忘錄&lt;/li&gt;
&lt;li&gt;Notion&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Evernote 是我用的第一個真正的個人資料庫，不過也持續沒多久，而且那時候沒有很多要放 Code 的需求，加上一直有風聲說 Evernote 營運不佳（誰知道現在還活得好好的而且還進化了不少），後來買了 Mac 之後就搬到原生備忘錄（信仰最高）。&lt;/p&gt;
&lt;p&gt;至於搬到 Notion 的理由，就是上面提到的那些了。&lt;/p&gt;
&lt;p&gt;不過有時候簡單的協作上，我還是會和 HackMD 並用，畢竟對中文的支援比較友善，也比較接近純 Markdown 編輯沒有太多複雜功能。&lt;/p&gt;
&lt;p&gt;不知不覺已經用了半年了，現在 Notion 也對學生開放免費使用（2020 年 5 月更宣布&lt;strong&gt;全使用者免費，不再有 Block 數量限制&lt;/strong&gt;），只要使用學校信箱註冊就能直接免費訂閱，因此建議只要有興趣就來試看看吧。而且就算原本用了別的信箱註冊，也能直接更改登入信箱，就能免費使用了。&lt;/p&gt;
&lt;h2 id="notion-簡介"&gt;Notion 簡介&lt;/h2&gt;
&lt;p&gt;Notion 是一家始於 2016 年的舊金山的新創公司 &lt;a href="https://www.notion.so/about"&gt;Notion Labs Inc.&lt;/a&gt; 的主打產品，架在 AWS 上，目的是想創造一個簡單易用的 &lt;strong&gt;All-In-One workspace&lt;/strong&gt;，能夠個人做資料庫，也能夠讓團隊協作，並整合任何想要增加的內容到一個地方。&lt;/p&gt;
&lt;h2 id="特色"&gt;特色？&lt;/h2&gt;
&lt;h3 id="介於-word-和純-markdown-之間的方便性"&gt;介於 word 和純 Markdown 之間的方便性&lt;/h3&gt;
&lt;p&gt;Notion 的特色就是所有東西都是由 Block 這個物件組成，什麼東西都可以拖拉組合，排版方便，也支援多欄位。支援 Markdown 語法的同時，不會用 Markdown 的人也可以直接用滑鼠點。&lt;/p&gt;
&lt;p&gt;同時，你想得到的東西都可以插入，像是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Youtube 影片&lt;/li&gt;
&lt;li&gt;Google Drive 文件&lt;/li&gt;
&lt;li&gt;PDF 檔&lt;/li&gt;
&lt;li&gt;Spotify 歌單（KKBOX 不支援，真對不起大家）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="大量方便編輯的熱鍵"&gt;大量方便編輯的熱鍵&lt;/h3&gt;
&lt;p&gt;詳見 &lt;a href="https://dwye.dev/post/notion-tricks/"&gt;Notion 小技巧&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="支援-code-block"&gt;支援 Code Block&lt;/h3&gt;
&lt;p&gt;任何有在寫 Code 的人都需要的功能，我捨棄 Apple 內建備忘錄的原因。這是 Markdown 原本就有的功能，而 Notion（或是其他 Markdown-based 編輯器）加入了 Syntax highlighting。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/Notion/code-block.png" alt="notion code block"&gt;&lt;/p&gt;
&lt;h3 id="就和寫網頁一樣"&gt;就和寫網頁一樣：&lt;/h3&gt;
&lt;p&gt;可以善用上面的功能，做出一個跟網頁一樣的 Home Page:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/Notion/Untitled.png" alt="notion personal home page"&gt;&lt;/p&gt;
&lt;p&gt;我的個人頁面（施工中）&lt;/p&gt;
&lt;p&gt;（可能有些人覺得旁邊空空的，他有個選項，可以調整是否限制最大寬度。）&lt;/p&gt;
&lt;p&gt;而頁面裡面又可以插入頁面，一層一層的，就像是把自己的資料庫做成網頁一樣，而不是一般筆記軟體的「分類 &amp;gt; 頁面」兩層式的架構。自訂的自由度相當高。&lt;/p&gt;
&lt;p&gt;就像是自己的 wiki 自己寫那樣。&lt;/p&gt;
&lt;h3 id="筆記分享"&gt;筆記分享&lt;/h3&gt;
&lt;p&gt;你可以把你寫好的筆記藉由網址分享給別人。&lt;/p&gt;
&lt;p&gt;&lt;del&gt;不是前端工程師也能做出漂亮的網頁ㄛ&lt;/del&gt;&lt;/p&gt;
&lt;h3 id="大量的模板"&gt;大量的模板&lt;/h3&gt;
&lt;p&gt;我懶得自己手刻頁面怎麼辦？沒關係，Notion 有大量的模板可以使用：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/Notion/Untitled%201.png" alt="notion template"&gt;&lt;/p&gt;
&lt;h3 id="包山包海"&gt;包山包海&lt;/h3&gt;
&lt;p&gt;沒錯，只要有上面這些模板，加上強大的客製化，可以取代掉很多軟體，像是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;待辦事項&lt;/li&gt;
&lt;li&gt;記帳&lt;/li&gt;
&lt;li&gt;Board&lt;/li&gt;
&lt;li&gt;行事曆&lt;/li&gt;
&lt;li&gt;也可以透過共用筆記達到像 Dropbox Paper 或 HackMD 那樣的協作功能！&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="版本紀錄"&gt;版本紀錄&lt;/h3&gt;
&lt;p&gt;要是改錯了怎麼辦？跟 Google Drive 一樣，可以一鍵復原！而且可以預覽各個版本的內容，就像 Mac 的 Time Machine 一樣。個人版支援 30 天歷史紀錄，如果是貴得要命的企業版(20 USD per month)，可以無上限還原。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/Notion/Untitled%202.png" alt="version control"&gt;&lt;/p&gt;
&lt;p&gt;支援版本紀錄與還原。&lt;/p&gt;
&lt;p&gt;當然也像維基百科那樣可以查看最近的編輯：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/Notion/Untitled%203.png" alt="version control and edited"&gt;&lt;/p&gt;
&lt;p&gt;最近編輯歷史。&lt;/p&gt;
&lt;h2 id="workspace-概念"&gt;Workspace 概念&lt;/h2&gt;
&lt;p&gt;跟 Slack 類似，Notion 支援一個帳號可以進出各個不同的工作區（或著是說這就是 Notion 出現的初衷！）。你可以跟朋友開工作區進行協作，&lt;del&gt;但我沒有朋友，所以沒有去嘗試。&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/Notion/Untitled%204.png" alt="notion workspace"&gt;&lt;/p&gt;
&lt;p&gt;左上角的選單。&lt;/p&gt;
&lt;h2 id="跨平台"&gt;跨平台&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Browser 打得開就可以用（&lt;a href="https://zh.wikipedia.org/wiki/%E8%BD%AF%E4%BB%B6%E5%8D%B3%E6%9C%8D%E5%8A%A1"&gt;SaaS&lt;/a&gt;）&lt;/li&gt;
&lt;li&gt;本地端 App:
&lt;ul&gt;
&lt;li&gt;MacOS、Windows 10、Android 手機、iPad 和 iPhone&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;網頁擷取外掛（Chrome、Firefox）&lt;/li&gt;
&lt;li&gt;手機一鍵加入筆記&lt;/li&gt;
&lt;li&gt;&lt;del&gt;Linux 哭哭&lt;/del&gt;，但可以用網頁版啦&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="快速搬遷"&gt;快速搬遷&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/Notion/Untitled%205.png" alt="notion workspace"&gt;&lt;/p&gt;
&lt;p&gt;所有的 import 選項。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;支援直接從競爭對手 evernote 把所有東西搬過來，搬了還有 5 USD 使用額度的獎勵，是多針對XDDD（像我就把五年前的陳舊筆記匯入之後刪掉，只為了 5 USD）&lt;/li&gt;
&lt;li&gt;也支援從 &lt;a href="http://hackmd.io"&gt;HackMD.io&lt;/a&gt; 直接打包 .md 檔案匯入（雖然格式會需要調整因為 HackMD 也不是用純的 Markdown）。&lt;/li&gt;
&lt;li&gt;也可以從專案管理軟體 Trello 和 Asana 把整個 Board 搬過來！&lt;del&gt;（每次都看成 &lt;a href="https://zh.moegirl.org/zh-tw/%E7%BB%93%E5%9F%8E%E6%98%8E%E6%97%A5%E5%A5%88"&gt;Asuna&lt;/a&gt;）&lt;/del&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="從備忘錄搬入"&gt;從備忘錄搬入&lt;/h3&gt;
&lt;p&gt;真的是很痛苦，因為 Apple 不支援批次運出，所以我自己找出了一個解決方案（雖然也不太完美）。有興趣的可以參考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dwye.dev/post/apple-note-to-notion/"&gt;從備忘錄搬移到 Notion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="推薦制度"&gt;推薦制度&lt;/h2&gt;
&lt;p&gt;你可以從這個 Link 註冊帳號，你跟我都可以直接拿到 10 USD 的使用額度獎勵！&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.notion.so/?r=20b5ae5c193c4b8f853993c53ba34486"&gt;The all-in-one workspace for your notes, tasks, wikis, and databases - Notion&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;如果以年份為單位訂閱的話，一個月是 4 USD，當然也可以用免費版，&lt;del&gt;這樣內容就有上限（而且挺容易爆的如果你跟我一樣筆記很多），但匯入的筆記不算在額度內，所以有個暗黑大法就是永遠用匯入來寫筆記（反正從 evernote 匯入做得這麼棒，不如多用幾次 XDD）。&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;(更新) &lt;a href="https://www.reddit.com/r/Notion/comments/gmrhlb/notion_is_now_free_for_personal_use/"&gt;&lt;strong&gt;2020 年 5 月 Notion 已經宣布免費使用者的 Block 數量無上限了&lt;/strong&gt;&lt;/a&gt;，只保留了上傳的檔案大小要在 5 MB 內的限制。&lt;/p&gt;
&lt;p&gt;這個推薦制度也間接鼓勵使用者寫文章來拉大家入坑，就像我這樣 XD&lt;/p&gt;
&lt;h2 id="搬出去"&gt;搬出去&lt;/h2&gt;
&lt;p&gt;鑑於 Apple 備忘錄那麼難搬，總是要關心一下如果哪天你不想用&lt;del&gt;或是 Notion 公司倒了&lt;/del&gt;該怎麼辦：&lt;/p&gt;
&lt;p&gt;Notion 支援以下三種匯出模式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PDF&lt;/li&gt;
&lt;li&gt;Markdown + csv&lt;/li&gt;
&lt;li&gt;HTML&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;三種匯出都能正常顯示內容，當然 .md 因為支援的比較少，特殊的 Block 會全部變成 Link。&lt;/p&gt;
&lt;p&gt;其中 PDF 版打包匯出是專業版功能，其他都可以直接打包匯出。&lt;/p&gt;
&lt;p&gt;上面說很像寫網頁，這邊就直接讓你匯出變成網頁了。真的 6。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/Notion/Untitled%206.png" alt="notion workspace"&gt;&lt;/p&gt;
&lt;p&gt;匯出之後的資料夾結構。&lt;/p&gt;
&lt;h2 id="缺點"&gt;缺點&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Webview 的 app 實在是有點吃效能，尤其是手機版，開得很慢。&lt;/li&gt;
&lt;li&gt;UI 沒有中文，但可以打中文啦。&lt;/li&gt;
&lt;li&gt;Markdown 匯出之後 Code Block 並非 fenced code block，所以 syntax highlighting 會消失。&lt;/li&gt;
&lt;li&gt;(更新) &lt;a href="https://dwye.dev/post/notion-privacy-concerns/"&gt;中國防火牆問題與隱私權爭議&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="想知道更多嗎"&gt;想知道更多嗎？&lt;/h2&gt;
&lt;h3 id="首先是官方完整的-guide"&gt;首先是官方完整的 Guide:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.notion.so/Help-Support-e040febf70a94950b8620e6f00005004"&gt;Notion Official: Help &amp;amp; Support&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="在-plurk-上轉發很多次的教學影片"&gt;在 Plurk 上轉發很多次的教學影片&lt;/h3&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/Q_PfYlAtvHc?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h3 id="其他人的文章"&gt;其他人的文章：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.inside.com.tw/article/16908-Notion.so-tips"&gt;矽谷大受歡迎的筆記軟體：Notion.so - INSIDE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.playpcesor.com/2018/10/notion.html"&gt;從零開始用 Notion 快速打造一個美觀又專業的旅行計畫筆記&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.playpcesor.com/2018/11/notion-google-trello-airtable.html"&gt;用 Notion 把筆記整理成像 Google 日曆、 Trello 看板與 Airtable 表格&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/plaintech/notion-9924dcf6c9db"&gt;文件排版＋個人資料庫＋專案管理三合一：筆記軟體 Notion 推薦&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=FcnXOicBo0M"&gt;23 Notion Tips, Hacks &amp;amp; Tricks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.playpcesor.com/2017/05/notion.html"&gt;Notion 把文件、任務、知識庫「無縫重組」的全新專案整理工具&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.playpcesor.com/2019/03/notion-evernote-web-clipper.html"&gt;Notion 筆記軟體推出網頁全文擷取外掛與自動匯入 Evernote&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/everyday-is-discovery-and-learning/notion-so-%E3%81%8C%E3%81%A8%E3%81%A6%E3%82%82%E8%89%AF%E3%81%84-f86d7e86e1bc"&gt;Notion.so がとても良い&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@Johann016/%E6%95%99%E5%AD%B8-%E4%B8%80%E5%B0%8F%E6%99%82%E5%8A%A0%E5%85%A5-notion-so-cd2b5ca8a7fe"&gt;從哪開始？一小時上手 Notion.so ⏱&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/jp%E7%B0%A1%E5%A0%B1%E5%9D%8A/notion%E6%95%99%E5%AD%B8-%E5%BE%9E%E5%85%A5%E9%96%80%E5%88%B0%E4%B8%8A%E6%89%8B%E6%93%8D%E4%BD%9C%E6%8A%80%E5%B7%A7-baa08ebf9069"&gt;Notion教學：從入門到上手操作技巧&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="資料庫欄位自訂語法"&gt;資料庫欄位自訂語法：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medium.com/@toiee_kame/notion-the-day-of-the-week-9429667975b1"&gt;Notion で「曜日」を表示する方法&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>Wercker 自動化部署 Hugo 網站</title><link>https://dwye.dev/post/hugo-wercker/</link><pubDate>Tue, 03 Sep 2019 00:01:56 +0800</pubDate><guid>https://dwye.dev/post/hugo-wercker/</guid><description>
&lt;p&gt;&lt;a href="https://dwye.dev/post/first-post/"&gt;架好這個網站後的第一篇文章&lt;/a&gt;提到，我是用 Wercker 做自動化部署。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://i.imgur.com/CdBoQ3C.png" alt="wercker"&gt;&lt;/p&gt;
&lt;p&gt;其實相關的說明很多，&lt;a href="https://gohugo.io/hosting-and-deployment/deployment-with-wercker/"&gt;Hugo 官方說明&lt;/a&gt;也很詳細，看得懂英文的可以參考，看不懂的看&lt;a href="http://handsomezhu.me/2017/10/hugo-blog-setup-tutorial/"&gt;這篇簡中的&lt;/a&gt;也行。&lt;/p&gt;
&lt;p&gt;不過這篇特別談的是 &lt;code&gt;wercker.yaml&lt;/code&gt; 到底要寫什麼？&lt;/p&gt;
&lt;p&gt;上面的官方說明附上了這個 script:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;box&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;debian&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;build&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;steps&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;arjen/hugo-build&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;version&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;0.17&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;theme&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;herring-cove&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;flags&lt;/span&gt;: --&lt;span style="color:#ae81ff"&gt;buildDrafts=true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;deploy&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;steps&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;install-packages&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;packages&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;git ssh-client&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;lukevivier/gh-pages@0.2.1&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;token&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;$GIT_TOKEN&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;domain&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;hugo-wercker.ig.nore.me&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;basedir&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;public&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;好像複製貼上改一改就可以了，不過遇到了一些問題。畢竟上面那段說明寫的時候，hugo 才 0.17 版，現在這篇文章是用 0.57 版在 build 的，&lt;del&gt;雖然還是一堆 bug 讓我有點想跳槽&amp;hellip;&lt;/del&gt;。&lt;/p&gt;
&lt;h2 id="git-submodule-設置"&gt;Git Submodule 設置&lt;/h2&gt;
&lt;p&gt;首先，我的 theme 是 &lt;a href="https://themes.gohugo.io/blackburn/"&gt;blackburn&lt;/a&gt;，不過其實我有用 submodule 放在我 local 端的 theme 資料夾裡（而且被我改了不少&amp;hellip;）：&lt;/p&gt;
&lt;script src="https://gist.github.com/dwy6626/09d96962a880be37c61cf5c7c48f247c.js"&gt;&lt;/script&gt;
&lt;p&gt;&lt;del&gt;（結果用了 Markdown 還是想插入 gist XDDDDD）&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;所以 &lt;code&gt;theme&lt;/code&gt; 那行可以拔掉。&lt;/p&gt;
&lt;p&gt;另外，因為使用了 git submodule，根據 &lt;a href="//devcenter.wercker.com/integrations/git/submodules/"&gt;Wercker 官方的說明&lt;/a&gt;，必須要手動把 submodule 給 &lt;code&gt;clone&lt;/code&gt; 下來（其實也是原本 &lt;code&gt;git clone&lt;/code&gt; 的特性，並不會自動幫導入 submodule），要在 &lt;code&gt;wercker.yml&lt;/code&gt; 把這一步寫入：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;script&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;install git&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;code&lt;/span&gt;: |&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; apt-get update
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; apt-get install git -y&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;add-ssh-key&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;keyname&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;KEY_NAME&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;host&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;github.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;add-to-known_hosts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;hostname&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;github.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;fingerprint&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;16&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;27&lt;/span&gt;:&lt;span style="color:#ae81ff"&gt;ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;script&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;initialize git submodules&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;code&lt;/span&gt;: |&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; git submodule update --init --recursive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;不過在 &lt;a href="https://www.kkstream.com/"&gt;KKStream&lt;/a&gt; 搞過一些簡單的 CI/CD 之後（你看在 KKStream 什麼都可以學，還不過來當 intern 嗎 XD），回來看到自己的 &lt;code&gt;wercker.yml&lt;/code&gt; 就覺得好像不需要這麼多步驟，真的需要再加一次 ssh key 嗎？還要加一次 known_hosts 嗎？不然一開始 build 的時候，怎麼把檔案從我的 private repo 拉下來的？所以就嘗試刪掉&amp;hellip;就成功了 XD&lt;/p&gt;
&lt;p&gt;順便把 &lt;code&gt;install git&lt;/code&gt; 那步，用 Wercker 自帶的 &lt;code&gt;install-packages&lt;/code&gt; 取代。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;install-packages&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;packages&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;git&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;於是就變成這樣了：&lt;/p&gt;
&lt;script src="https://gist.github.com/dwy6626/d072c4809db1e04e02e4eab9365e567e.js"&gt;&lt;/script&gt;
&lt;p&gt;至於為什麼 &lt;code&gt;$GIT_TOKEN&lt;/code&gt; 變成 &lt;code&gt;$GITHUB_TOKEN&lt;/code&gt;？那就是個環境變數名字，存在 Environment 那格裡面的，所有 pipeline 共用的變數。因為 &lt;a href="https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line"&gt;TOKEN 是在 GitHub 生成的&lt;/a&gt;，我覺得這樣叫比較合理而已。&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>為什麼我的 Markdown 會跑版？</title><link>https://dwye.dev/post/markdown-intro-history/</link><pubDate>Sun, 01 Sep 2019 00:41:23 +0800</pubDate><guid>https://dwye.dev/post/markdown-intro-history/</guid><description>
&lt;p&gt;這個問題困擾了我好一陣子，直到有朋友和我說，Markdown 有很多種標準，頓時茅塞頓開。既然程式語言有分版本，HTML 也有不同版本，那麼副檔名都是 &lt;code&gt;.md&lt;/code&gt; 的 Markdown 也有很多種版本，似乎也是合理的事情。&lt;/p&gt;
&lt;p&gt;那下一個問題就是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Markdown 有沒有一個&lt;strong&gt;統一&lt;/strong&gt;的標準？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;答案是&lt;strong&gt;沒有&lt;/strong&gt;的。&lt;/p&gt;
&lt;p&gt;而且也不會有，因為&lt;strong&gt;大家需要的 Markdown 可能不太一樣&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;最簡單的例子就是換行，就標準的 Markdown 語法，當你在 &lt;code&gt;.md&lt;/code&gt; 檔內換行，並不會真的換行。如果你要做到換行，必須在行尾加入兩個空白，或是手動插入 &lt;code&gt;&amp;lt;br&amp;gt;&lt;/code&gt;。不過像是台灣知名的線上筆記服務 &lt;a href="https://hackmd.io/"&gt;HackMD&lt;/a&gt;，就很貼心的配合了台灣人的打字習慣，幫你把每個換行都變成真正的換行。&lt;/p&gt;
&lt;p&gt;其實包含 HackMD 在內，大部分的 Markdown 服務都有切換換行模式的選項，為了滿足每種人。也因此 Markdown 不會有真正統一的標準。&lt;/p&gt;
&lt;p&gt;但是撇除這種選項式的差別之外，目前最知名的應該是 &lt;a href="https://commonmark.org/"&gt;CommonMark&lt;/a&gt; 了，若要問為什麼叫 CommonMark 這個名字？而 CommonMark 跟一般的 Markdown 有什麼差別？就必須從 Markdown 的歷史說起。&lt;/p&gt;
&lt;h2 id="the-origin"&gt;The Origin&lt;/h2&gt;
&lt;p&gt;一切的開端，是 2004 時 &lt;a href="https://daringfireball.net/projects/markdown/syntax"&gt;John Gruber 提出的 Markdown 標準&lt;/a&gt;，也是一般所說的經典的 Markdown，以 BSD 開源。&lt;/p&gt;
&lt;h3 id="markdown-的理念"&gt;Markdown 的理念&lt;/h3&gt;
&lt;p&gt;John Gruber 的文章寫道：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The overriding design goal for Markdown&amp;rsquo;s formatting syntax is to make it as readable as possible. The idea is that a Markdown-formatted document should be publishable as-is, as plain text, without looking like it&amp;rsquo;s been marked up with tags or formatting instructions.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;簡單來說，就是想要創造一個易讀而且易寫的格式，也因此和 reStructuredText 一同被歸類為輕量級的標記語言（當然不只這兩種，可以參考 &lt;a href="https://github.com/github/markup"&gt;GitHub 支援列表&lt;/a&gt;）。比起更早出現的 reStructuredText，Markdown 似乎又更簡單一些，雖然 Markdown 比起 reStructuredText 的功能較少，而且標準化也較晚，但並不影響 Markdown 受歡迎的程度。&lt;/p&gt;
&lt;p&gt;John Grube 以 Perl 寫了第一個 Markdown to html 轉換器，一般被稱為 &lt;code&gt;Markdown.pl&lt;/code&gt;，但沒多久就沒有繼續維護了。而且隨著使用的人越來越多，漸漸有定義相關的問題產生出來，於是後來群雄並起，產生了各種分支。&lt;/p&gt;
&lt;p&gt;原始 Markdown 的語法的中文翻譯可以看&lt;a href="https://markdown.tw/"&gt;這個網站&lt;/a&gt;。或是 &lt;a href="https://www.markdownguide.org/basic-syntax"&gt;Markdown Guide 這邊有個英文的對照表&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;值得一提的是，這時候的 Markdown 的 Code Block 並不是大家熟悉的那種 &lt;code&gt;```&lt;/code&gt; 開頭並且可以註記語言種類的 fenced code block：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;```javascript
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;Hello, markdown!&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;```&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;而是利用縮排的 indented code block，並不支援 syntax highlighting（因為不能註記語言種類）。注意前面有四個空格（或是一個 &lt;code&gt;tab&lt;/code&gt;）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;Hello, markdown!&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="markdown-extra"&gt;Markdown Extra&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://six.pairlist.net/pipermail/markdown-discuss/2005-July/001379.html"&gt;2005 年由 Michel Fprtin 發表&lt;/a&gt;，基於原始 Markdown 並修改及新增一些功能，並在當時希望這些能成為未來 Markdown 的標準（下面的 CommonMark 和 GFM 都分別和 Markdown Extra 有一些相同的特色），除了表格之外，其中比較特別的功能是支援腳註（Footnotes）和縮寫。官方編譯是 &lt;a href="https://github.com/michelf/php-markdown"&gt;PHP Markdown&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;因為不是這篇的重點，詳細介紹可以到&lt;a href="https://michelf.ca/projects/php-markdown/extra/"&gt;他們的網站&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id="multimarkdown-mmd"&gt;MultiMarkdown (MMD)&lt;/h2&gt;
&lt;p&gt;就在 Markdown Extra 發表的隔天，&lt;a href="http://six.pairlist.net/pipermail/markdown-discuss/2005-July/001389.html"&gt;Fletcher T. Penney 發表了 MultiMarkdown&lt;/a&gt;，同樣也是基於原始 Markdown，希望能夠變得更接近像是 XHTML 或是 LaTeX 之類的排版語言，可以看&lt;a href="https://fletcherpenney.net/multimarkdown/"&gt;作者的介紹&lt;/a&gt;：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It adds multiple syntax features (tables, footnotes, and citations, to name a few), in addition to the various output formats listed above (Markdown only creates HTML). Additionally, it builds in “smart” typography for various languages (proper left- and right-sided quotes, for example).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;一樣支援表格和腳註等功能，但為了進一步進行更複雜的編寫，也支援引用、數學式、圖片說明等等功能。另外也支援更多的輸出格式，例如 PDF 和 OPML（一種開放式文件檔案格式，可以進一步讓 Microsoft Word 使用）。官方編譯器是原始的 &lt;code&gt;Markdown.pl&lt;/code&gt; 的延伸，存放在&lt;a href="https://github.com/fletcher/MultiMarkdown"&gt;這個 repo&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id="commonmark"&gt;CommonMark&lt;/h2&gt;
&lt;p&gt;因為原始 Markdown 就這樣缺乏後續的維護，因此就有人提出要接續幫 Markdown 進行語法的標準化以及規範的進一步維護，這就是 &lt;a href="https://commonmark.org/"&gt;CommonMark&lt;/a&gt; 的開端。從 2012 年開始，包括 Jeff Atwood（&lt;a href="https://stackoverflow.com/"&gt;Stack Overflow&lt;/a&gt; 的共同創辦者之一） 在內的一群人啟動了所謂的 Markdown 標準化工作（&lt;a href="https://blog.codinghorror.com/the-future-of-markdown/"&gt;Standard Markdown&lt;/a&gt;，這個專案原本的名字，&lt;a href="https://blog.codinghorror.com/standard-markdown-is-now-common-markdown/"&gt;但是在原作者要求之下改名為 CommonMark&lt;/a&gt;，還為此叫他們換掉原本的 domain 並且不能直接重新導向到新網站）。&lt;/p&gt;
&lt;p&gt;根據 Jeff Atwood （註：高中力學教的阿特午機的阿特午，英文也是 Atwood）的 &lt;a href="https://blog.codinghorror.com/the-future-of-markdown/"&gt;blog 文章&lt;/a&gt;，這個計畫有以下的目標：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;定義&lt;a href="https://spec.commonmark.org/current/"&gt;更明確的 spec&lt;/a&gt;，把可能有爭議的語法全部定義清楚要怎麼轉換成 HTML（所以這個檔案超長）&lt;/li&gt;
&lt;li&gt;新增一些選項，可以被關閉 / 開啟以符合各種需求，例如：
&lt;ul&gt;
&lt;li&gt;intra-word emphasis：字串內部的強調是否要用空格隔開&lt;/li&gt;
&lt;li&gt;auto-hyperlink：是否自動為文章中的網址建立超連結&lt;/li&gt;
&lt;li&gt;automatic return-based linebreaks：是否要把 return 視為換行&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/commonmark/commonmark-spec"&gt;測試集&lt;/a&gt;（和 spec 放在同一個 repo）&lt;/li&gt;
&lt;li&gt;整理已知的變體（&lt;del&gt;雖然 CommonMark 本身就是個變體&lt;/del&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="因此-commonmark-和原始的-markdown-有這些差別"&gt;因此 CommonMark 和原始的 Markdown 有這些差別：&lt;/h3&gt;
&lt;p&gt;新增項目（有許多並非 CommonMark 的創舉，而是很多已經在使用的 Markdown 已經先加入了）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;所有 symbol 都可以被 backslash-escaped（中文叫跳脫字元，為了防止和保留字元混淆，因此在打某些特定符號的時候，例如&lt;code&gt;\&lt;/code&gt;的時候，必須要打成&lt;code&gt;\\&lt;/code&gt;。），因為他們發現一般人很難記得哪些字元要 escape XD&lt;/li&gt;
&lt;li&gt;加入 backslash 換行機制（原本只能雙空白，很難在文字編輯器上面被注意到）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1)&lt;/code&gt;也可以被當作數字列表（原本只能用 &lt;code&gt;1.&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;允許非 1 開始的數字列表&lt;/li&gt;
&lt;li&gt;加入大家很常用的 Fenced code blocks：用 &lt;code&gt;```&lt;/code&gt; 和 &lt;code&gt;~~~&lt;/code&gt; 開頭&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;更動：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;當使用了不同的 list 的符號，會被判斷成分開的 list（例如： &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt; 以及 &lt;code&gt;1.&lt;/code&gt;, &lt;code&gt;1)&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;gt;&lt;/code&gt; 用空行分開後，會被判斷成分開的 Quote，而不是合併成一個&lt;/li&gt;
&lt;li&gt;Link Syntax 改善，e.g. 單引號也可以被用在 Reference Link 的 Title 裡面（原本只有 Inline Link 可以）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;CommonMark 旨在&lt;strong&gt;建立一個嚴謹的標準，而不是支援更多的功能&lt;/strong&gt;，雖然上面有提到新增的語法，但是那些都是替代性語法，並不是真正的新功能。也因此，只要是原始 Markdown 沒有的功能， CommonMark 也不會有。&lt;/p&gt;
&lt;p&gt;官方提供的編譯器有兩個，一個是 &lt;a href="https://github.com/commonmark/cmark"&gt;cmark&lt;/a&gt;，一個是 &lt;a href="https://github.com/commonmark/commonmark.js"&gt;CommonMark.js&lt;/a&gt;，從名稱就能看出是基於什麼語言、或是給什麼語言用的了。&lt;/p&gt;
&lt;h2 id="github-flavored-markdown-gfm"&gt;GitHub Flavored Markdown (GFM)&lt;/h2&gt;
&lt;p&gt;GFM，中文意思是「GitHub 偏好的 Markdown」，&lt;a href="https://github.blog/2017-03-14-a-formal-spec-for-github-markdown/"&gt;2017 年發布&lt;/a&gt;，基於 CommonMark，再加上一些 GitHub 上可能會用到的功能，簡單來說，就是 GitHub 現在正在用的 Markdown（但其實在 GFM 之前的 2012 年，GitHub 就有支援 Markdown 了）。&lt;/p&gt;
&lt;h3 id="主要擴充"&gt;主要擴充：&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://github.github.com/gfm/"&gt;GFM&lt;/a&gt; 的規範在此，這邊僅列出和 CommonMark 不同之處（其實它就是直接從 CommonMark 的 &lt;a href="https://github.com/github/cmark-gfm"&gt;cmark 拉個分支出來&lt;/a&gt;）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.github.com/gfm/#tables-extension-"&gt;4.10 表格&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.github.com/gfm/#task-list-items-extension-"&gt;5.3 任務列表&lt;/a&gt;（主要給 issue 用的）&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.github.com/gfm/#strikethrough-extension-"&gt;6.5 刪除線&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.github.com/gfm/#autolinks-extension-"&gt;6.9 自動超連結的更改&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.github.com/gfm/#disallowed-raw-html-extension-"&gt;6.11，過濾掉一些 HTML tag&lt;/a&gt;，將裡面的 &lt;code&gt;&amp;lt;&lt;/code&gt; 取代成 &lt;code&gt;&amp;amp;lt;&lt;/code&gt;，似乎是 GitHub 認為這些標籤在編譯的時候在某些組合會有特殊情況發生。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="關於-hackmd"&gt;關於 HackMD&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://dwye.dev/img/Untitled-46f3245a-2064-4a23-8ffa-225aba38fdd7.png" alt="HackMD"&gt;&lt;/p&gt;
&lt;p&gt;在台灣寫一篇 Markdown 的文章，不能不提一下 &lt;a href="https://hackmd.io/"&gt;HackMD&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;HackMD 是基於 Markdown 之上（其實他們並沒有說清楚是基於哪種 Markdown 之上，不過你想的到的功能他們都有，甚至還能插入 YouTube 影片），加上一堆很 fancy 的語法，又是台灣出產的線上筆記服務，只要打開瀏覽器就能使用，而且還能即時預覽！&lt;/p&gt;
&lt;p&gt;有開源版本 &lt;a href="https://demo.codimd.org/"&gt;CodiMD&lt;/a&gt;，也提供企業版的 SaaS （Software-as-a-Service），還能把版本歷史整合進 GitHub，讓大家愛上 Markdown 的好地方。&lt;/p&gt;
&lt;p&gt;介紹的部分我就不多敘述，隨手 Google 兩篇看起來不錯的介紹：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.playpcesor.com/2016/10/hackmd-hackpad-markdown.html"&gt;HackMD 取代 Hackpad 的中文版 Markdown 共筆平台上手教學&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/starrocket/hackmd-product-story-1e332f83d343"&gt;為工程師文件而生的協作平台：HackMD 開發故事&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;個人認為在台灣的 Markdown 推廣，HackMD 一定會扮演一個重要的角色，因為真的太好用了，而且又免費！根本佛心來著。&lt;/p&gt;
&lt;h2 id="小結"&gt;小結&lt;/h2&gt;
&lt;p&gt;Markdown 有各種規範，也有各種選項可以調整，像是&lt;a href="http://dwy6626.github.io"&gt;我的網站&lt;/a&gt;是基於 Hugo，一個 Go 編寫的靜態網站生成器（SSG），&lt;del&gt;所以採用了同樣是 Go 編寫的 &lt;a href="https://github.com/russross/blackfriday"&gt;blackfriday&lt;/a&gt; 來編譯&lt;/del&gt;（0.60 版之後的 Hugo 已經改為使用 &lt;a href="https://github.com/yuin/goldmark/"&gt;Goldmark&lt;/a&gt; 作為 Markdown 編譯器了，詳情在這篇文章：&lt;a href="https://gohugo.io/news/0.60.0-relnotes/"&gt;Now CommonMark Compliant&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;為了能夠方便寫作，我在我的網站把可調選項（例如換行）調整成和 HackMD 的選項一樣，希望能夠最大化相容，並且最小化需要額外修改的地方。&lt;/p&gt;
&lt;p&gt;寫了這麼多，希望大家在處理各種 &lt;code&gt;.md&lt;/code&gt; 檔的時候，在享受 Markdown 帶來的便利（和潮感）之外，不要跟我一樣四處踩雷 QQ&lt;/p&gt;
&lt;h2 id="參考資料"&gt;參考資料&lt;/h2&gt;
&lt;p&gt;我在寫這篇的時候讀的文章，有些連結附在文章裡面了，就不再貼一次：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.othree.net/log/2017/03/27/github-flavored-markdown/"&gt;GitHub Flavored Markdown 標準規範&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.jianshu.com/p/03dccaae11d7"&gt;关于Markdown，你应该知道的&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.veekxt.com/article/7"&gt;VeekXT&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.markdownguide.org/extended-syntax"&gt;Extended Syntax&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://aaronbeveridge.com/markdown/history.html"&gt;Markdown Movement&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>茜雫凜 (Senna Rin) 介紹</title><link>https://dwye.dev/post/senna-rin-intro/</link><pubDate>Sun, 04 Aug 2019 17:21:28 +0800</pubDate><guid>https://dwye.dev/post/senna-rin-intro/</guid><description>
&lt;p&gt;&lt;img src="https://pbs.twimg.com/media/D_1PNKMUwAAnuid?format=jpg" alt="senna rin"&gt;&lt;/p&gt;
&lt;p&gt;&lt;ruby&gt;茜雫&lt;rt&gt;せんな&lt;/rt&gt;凜&lt;rt&gt;りん&lt;/rt&gt;&lt;/ruby&gt;是日本一個 cover 日文歌的 JK（女子高中生），九州的&lt;a href="https://twitter.com/senna_rin/status/1023887599499276288"&gt;福岡人&lt;/a&gt;，目前高中二年級，&lt;a href="https://twitter.com/senna_rin/status/1040211770495782913"&gt;生日是 9 月 23 日&lt;/a&gt; &lt;del&gt;（和我一樣是天秤）&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;透過 Youtube 發表作品，發表的 cover 主要以流行歌為主，有簡單的吉他彈唱 cover，也有重新編曲過的，錄音和 MV 品質都蠻好的，唱的當然也很好！&lt;/p&gt;
&lt;p&gt;常 cover 的歌手/團體有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Aimer&lt;/li&gt;
&lt;li&gt;米津玄師&lt;/li&gt;
&lt;li&gt;RADWIMPS&lt;/li&gt;
&lt;li&gt;Mr. Children&lt;/li&gt;
&lt;li&gt;Back number&lt;/li&gt;
&lt;li&gt;aiko&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="頻道名稱-こもれびちゃんねる"&gt;頻道名稱 「こもれびちゃんねる」&lt;/h2&gt;
&lt;p&gt;「こもれび」大概就是「木漏れ日」，日文維基百科這樣寫的：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;森林などの木立ちから太陽の日差しが漏れる光景のこと。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;森林中從木葉縫隙中滲入的陽光所形成的景象，在我想像大概是這樣：&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/Ci8welIUNrQ?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;「ちゃんねる」就是頻道，所以大概可以理解成「林間隙光的頻道」，也正好凛さん頻道裡面的歌，大多都是輕柔系的，當然也有些帶著淡淡的哀傷，像是&lt;a href="https://lyrics-blog.github.io/post/ai-no-uta/"&gt;AIのうた&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;凛さん的聲音其實蠻療癒的，悲傷的歌也不會太過沈重那種，很溫柔的聲音，很適合一首一首聽下去。&lt;/p&gt;
&lt;h2 id="翻唱內容"&gt;翻唱內容&lt;/h2&gt;
&lt;p&gt;從 2017 年上傳第一部 &lt;a href="https://www.youtube.com/watch?v=y7UZnI8xWKo"&gt;Himawari&lt;/a&gt; 開始，我寫這篇的時候已經有 41 首 cover，而且陸續增加中。&lt;/p&gt;
&lt;p&gt;其中播放次數超過千萬的一首：&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/PdE4P-hm2hg?autoplay=1&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=1&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;當然不能否認也是搭上 Aimer 的人氣，但是 Aimer 這首歌的 MV 才 100 萬次閱覽，這首 cover 是十倍人氣啊！！&lt;br&gt;
&lt;del&gt;今年是十倍嗎？？&lt;/del&gt;&lt;br&gt;
這首歌也是我認識凛さん的原因。&lt;/p&gt;
&lt;p&gt;偶爾會有些自創曲，目前已經有四首了，這首也是其中一首。&lt;br&gt;
自創曲完成度都意外的很高，編曲/錄音/拍攝都有一定水準，完全就是以後順著出道也不意外的類型，而且也累積了不少粉絲了，雖然本人最近才在 IG 限時動態的 QA 說沒有事務所就是。&lt;/p&gt;
&lt;p&gt;十六萬訂閱，除了日本國內之外，外國也有不少粉絲，之前 IG 直播（直播其實蠻稀有的，畢竟凛さん是個高中生，剛好我在下班時間的車上看到的）的時候，有些人和我一樣在報國籍 XDDD&lt;/p&gt;
&lt;p&gt;一開始活動的時候名字只有「凛」一個字，後來才加上姓氏「茜雫」，讀作&lt;del&gt;西卡&lt;/del&gt;才不對，是 Senna，合起來就是 &lt;strong&gt;Senna Rin&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;最近開了新的&lt;a href="https://www.youtube.com/channel/UCF3GFSms-WSRmJKvOgLC7mw"&gt;頻道&lt;/a&gt;，不知道為什麼不是選擇改名，可能有什麼新的個人計畫吧～&lt;/p&gt;
&lt;p&gt;但這邊就比較少人了，路過幫點一下支持吧，這是天氣之子的電影主題曲之一「グランドエスケープ」：&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/LPP5dd-Oat0?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2 id="其他"&gt;其他&lt;/h2&gt;
&lt;p&gt;上面那些個人資料其實不用特別去查，本人都有說 XD&lt;/p&gt;
&lt;blockquote class="twitter-tweet"&gt;&lt;p lang="ja" dir="ltr"&gt;福岡！&lt;/p&gt;&amp;mdash; 茜雫凛 (@senna_rin) &lt;a href="https://twitter.com/senna_rin/status/1023889541319471104?ref_src=twsrc%5Etfw"&gt;July 30, 2018&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;
&lt;!-- ![](https://i.imgur.com/QtMWhOo.png)
![](https://i.imgur.com/zeWuWgo.png) --&gt;
&lt;blockquote class="twitter-tweet"&gt;&lt;p lang="und" dir="ltr"&gt;23💕&lt;/p&gt;&amp;mdash; 茜雫凛 (@senna_rin) &lt;a href="https://twitter.com/senna_rin/status/1040213127357296640?ref_src=twsrc%5Etfw"&gt;September 13, 2018&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>讓 VSCode 的 Terminal 執行 Conda 的 Python</title><link>https://dwye.dev/post/vscode-python-terminal/</link><pubDate>Mon, 29 Jul 2019 15:47:44 +0800</pubDate><guid>https://dwye.dev/post/vscode-python-terminal/</guid><description>
&lt;p&gt;其實這個問題一直都有在困擾我，不過因為通常都是用 iTerm 另外開 Terminal 測試，或是直接用 VSCode 的 Python Plug-in 執行，所以就比較還好。因為想寫 Side Project，今天決定先認真來解決這個問題。&lt;/p&gt;
&lt;p&gt;於是就找到了這個 issue:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/Microsoft/vscode-python/issues/4434"&gt;https://github.com/Microsoft/vscode-python/issues/4434&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;裡面的 &lt;a href="https://github.com/praenubilus"&gt;praenubilus&lt;/a&gt; 這位大大有說到，原因是 VSCode 在啟動 Terminal 時，可能有特別的 Shell Initialization，這會導致 Conda 的 &lt;code&gt;$PATH&lt;/code&gt; 被排在後方，而非前方，可以透過 &lt;code&gt;echo $PATH&lt;/code&gt; 指令來查看：&lt;/p&gt;
&lt;p&gt;這是一般使用 iTerm 開的：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;/Users/xxx/miniconda3/bin:/Users/xxx/miniconda3/condabin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;這是使用 VSCode 的內建 Terminal：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/xxx/miniconda3/bin:/Users/xxx/miniconda3/condabin
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="一個-magic-workaround"&gt;一個 Magic Workaround&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/praenubilus"&gt;praenubilus&lt;/a&gt; 也提供了一個一個簡單的暫時改善方法：&lt;br&gt;
在 Setting 裡面搜尋 &lt;code&gt;terminal.integrated.env.osx&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://i.imgur.com/4S3p7Hq.png" alt="terminal search result"&gt;&lt;/p&gt;
&lt;p&gt;因為好像不能直接在 GUI 修改，所以我是在 Edit in settings.json 打開設定檔，加入：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;#34;terminal.integrated.env.osx&amp;#34;: {
&amp;#34;PATH&amp;#34;: &amp;#34;&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;這樣就可以得到一個&lt;strong&gt;乾淨的Terminal&lt;/strong&gt;（參見這篇 issue: &lt;a href="https://github.com/microsoft/vscode/issues/70248"&gt;Support using a &amp;ldquo;clean&amp;rdquo; environment for the terminal&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;同時打開 Terminal 時，也可以正常找到 Conda 的 Python，可以透過 &lt;code&gt;which python&lt;/code&gt; 指令來確認，應該要得到下面的結果：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;/usr/local/miniconda3/bin/python
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;至於正式的改善方案&amp;hellip;還是個 Open issue:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/microsoft/vscode/issues/47816"&gt;Feature Request: Reload environment variables on new integrated terminal instance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>未了</title><link>https://dwye.dev/post/sodagreen-endless/</link><pubDate>Sat, 08 Jun 2019 01:26:27 +0800</pubDate><guid>https://dwye.dev/post/sodagreen-endless/</guid><description>
&lt;p&gt;或許或多或少，或是不記得主角的名字&lt;br&gt;
但也許你聽過一個希臘神話&lt;br&gt;
關於一個罪人被神降下永恆的勞動懲罰&lt;br&gt;
勞動的內容，是推動巨石上山&lt;br&gt;
當這個任務完成後，巨石就會滾下山&lt;br&gt;
於是形成無止盡迴圈&lt;/p&gt;
&lt;p&gt;如果用一個什麼故事都是有結束的觀點來看&lt;br&gt;
那麼這樣的懲罰是沒有止盡的痛苦&lt;br&gt;
沒有結局，不斷重複&lt;br&gt;
無論怎麼努力，都看不到盡頭&lt;/p&gt;
&lt;p&gt;這首歌是在描述另外一個觀點&lt;br&gt;
即使沒有迎來盡頭&lt;br&gt;
過程的本身也是生命的一部分：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;雖然反覆，卻漸漸懂得：每一步都是自己的；&lt;br&gt;
不愛永恆，但求現在：真實活著的人生。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;走出悲傷什麼的，總是會這麼說&lt;br&gt;
因為想要擺脫現在的困境&lt;br&gt;
所以焦躁地想要做些什麼&lt;br&gt;
但實際上不可能物理上地與自己分割&lt;br&gt;
不管怎麼樣的自己，都是自己的一部分&lt;/p&gt;
&lt;p&gt;也或許當有什麼走不出的情緒&lt;br&gt;
其實也不存在走出去的一天&lt;br&gt;
所以換個角度&lt;br&gt;
學習與那個自己共存&lt;br&gt;
學會活用那樣負面的情緒所帶來的力量&lt;br&gt;
比起拋下或是脫離什麼的&lt;br&gt;
更是真實活著的人生吧&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/xiUc8m5lxcQ?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>在 Jupyter 使用虛擬 Python 環境</title><link>https://dwye.dev/post/venv-jupyter/</link><pubDate>Mon, 04 Mar 2019 23:41:25 +0800</pubDate><guid>https://dwye.dev/post/venv-jupyter/</guid><description>
&lt;p&gt;因為交作業要有特定套件，但調參數還是用 Jupyter 方便，所以就研究了下怎麼用特定的 Python 來執行 Jupyter Notebook 或是 JupyterLab。方法有兩種，首先我們先從創建虛擬環境講起：&lt;/p&gt;
&lt;h2 id="準備"&gt;準備&lt;/h2&gt;
&lt;h3 id="創建虛擬環境"&gt;創建虛擬環境&lt;/h3&gt;
&lt;p&gt;這邊以 conda 為例：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;conda create --name new_env python&lt;span style="color:#f92672"&gt;=&lt;/span&gt;3.6
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;輸入 y（或是直接 enter）就完成這步了，可以看一下視窗內部的訊息來確認。或是列出現有的環境：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;conda env list
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="啟動虛擬環境"&gt;啟動虛擬環境&lt;/h3&gt;
&lt;p&gt;確認你的 .bashrc（或是 macOS 上：.bash_profile）有包含你的虛擬環境的路徑：（以我的 miniconda 為例）&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# .bash_profile&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export PATH&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;/usr/local/miniconda3/bin:&lt;/span&gt;$PATH&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;接著用這行指令就可以直接啟動了：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;conda activate new_env
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這時候命令列的前方會多出一個 &lt;code&gt;(new_env)&lt;/code&gt;，就代表我們已經進入這個虛擬環境了，但這時候我們的 Python 還是系統預定的版本，記得也為他安裝一個 Python：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;conda install python&lt;span style="color:#f92672"&gt;=&lt;/span&gt;你要安裝的版本
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="第一招在原本的-jupyter-中加入新的-kernel"&gt;第一招：在原本的 Jupyter 中加入新的 Kernel&lt;/h2&gt;
&lt;p&gt;首先照著上面做的進入虛擬環境，然後安裝 ipykernel：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;conda install ipykernel
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;沒意外的話會有一大堆相依套件被安裝，按 y 繼續就好。&lt;br&gt;
接下來確認一下你的 Python 是正確的 Python：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; which python
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/usr/local/miniconda3/envs/new_env/bin/python
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;正確的話就可以直接幫 Jupyter 增加新的 kernel：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;python -m ipykernel install --name another_name
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;接著列出可以使用的 kernel，檢查是否成功：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;jupyter kernelspec list
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;就會看到剛剛加入的 kernel&lt;/p&gt;
&lt;p&gt;&lt;img src="//i.imgur.com/mVfFZOD.png" alt="jupyter kernel list"&gt;&lt;/p&gt;
&lt;h3 id="切換-kernel"&gt;切換 kernel&lt;/h3&gt;
&lt;p&gt;可以直接在新增的時候選擇 kernel，或是進入 notebook 之後再修改 kernel。&lt;/p&gt;
&lt;p&gt;Jupyter lab 選擇頁面：&lt;br&gt;
&lt;img src="//i.imgur.com/fCC5TdM.png" alt="Jupyter lab 選擇頁面"&gt;&lt;/p&gt;
&lt;p&gt;notebook 切換 kernel：&lt;br&gt;
&lt;img src="//i.imgur.com/w7I31we.png" alt="notebook 切換 kernel"&gt;&lt;/p&gt;
&lt;h2 id="第二招為虛擬環境安裝獨立的-jupyter"&gt;第二招：為虛擬環境安裝獨立的 Jupyter&lt;/h2&gt;
&lt;p&gt;一樣先進入虛擬環境，然後直接在裡面安裝 Jupyter：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;conda install jupyter
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;沒啥特別好說的，你開心也可以用 pip。然後去泡杯茶等它安裝好就行了。&lt;/p&gt;
&lt;p&gt;這時候你的 Jupyter 應該就會被置換成虛擬環境的版本，可以用 which 確認一下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt; which jupyter
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/usr/local/miniconda3/envs/new_env/bin/jupyter
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;接下來就跟平常一樣，走進你喜歡的根目錄之後開啟 Jupyter，就大功告成了&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>使用 Bcc 保護收件人的隱私</title><link>https://dwye.dev/post/bcc/</link><pubDate>Wed, 16 Jan 2019 21:00:20 +0800</pubDate><guid>https://dwye.dev/post/bcc/</guid><description>
&lt;!-- 寄信的學問：To? CC? BCC? --&gt;
&lt;p&gt;最近發生太多被洩露 email 的案件，決定來寫一篇文。&lt;/p&gt;
&lt;p&gt;寄信也是有很多學問的，這篇只是其中一環。&lt;/p&gt;
&lt;p&gt;&lt;a href="https://hackmd.io/s/Bk-jM3hMV"&gt;HackMD version&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="toccbcc"&gt;To/Cc/Bcc?&lt;/h2&gt;
&lt;h3 id="收件者-to"&gt;收件者 To&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;主要需要收到你這封信的人（們）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="副本-carbon-copy-cc"&gt;副本 Carbon Copy (Cc)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;把你的信件複製一份給別人做參考&lt;/li&gt;
&lt;li&gt;收件者和被 Cc 的人彼此看得到對方&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="密件副本-blind-carbon-copy-bcc"&gt;密件副本 Blind Carbon Copy (Bcc)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;把你的信件複製一份給別人做參考&lt;/li&gt;
&lt;li&gt;被 Bcc 的人不會被看見&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="為什麼要用-bcc"&gt;為什麼要用 Bcc&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;收件者彼此之間會看到其他人的 email（甚至是名字）&lt;/li&gt;
&lt;li&gt;隱私問題！&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ptt.cc/bbs/LoL/M.1536386502.A.636.html"&gt;[閒聊] Reddit 熱議: NA LCS 通知出包&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="什麼時候該用-bcc"&gt;什麼時候該用 Bcc&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;收件者有一堆人&lt;/li&gt;
&lt;li&gt;收件者彼此不認識&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="什麼時候不該用-bcc"&gt;什麼時候不該用 Bcc&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;工作小組討論問題&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="怎麼用-bcc"&gt;怎麼用 Bcc&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;收件者/Cc/Bcc 會有三欄不同的名單&lt;/li&gt;
&lt;li&gt;以 Mac 的郵件為例：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://i.imgur.com/j6JSo7b.jpg" alt=""&gt;&lt;/p&gt;
&lt;h2 id="延伸閱讀"&gt;延伸閱讀&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://lastparadise.pixnet.net/blog/post/24727859-bcc-%E6%98%AF%E4%B8%80%E7%A8%AE%E5%9F%BA%E6%9C%AC%E7%A6%AE%E8%B2%8C"&gt;BCC 是一種基本禮貌&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.bnext.com.tw/ext_rss/view/id/651536"&gt;解讀 Google 執行董事長 Eric Schmidt 的 9 條 Email 規則&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ptt.cc/man/NTUHistory89/D7B4/DA50/D7AF/M.981269465.A.html"&gt;養成 Email 寄信好習慣，確保隱私資料不外流&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://cc.nchu.edu.tw/content/docs/%E5%AF%86%E4%BB%B6%E5%89%AF%E6%9C%AC%E7%B0%A1%E4%BB%8B%E8%88%87%E4%BD%BF%E7%94%A8"&gt;中興大學 密件副本簡介與使用&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>為什麼會有這個網站</title><link>https://dwye.dev/post/first-post/</link><pubDate>Thu, 10 Jan 2019 14:34:33 +0800</pubDate><guid>https://dwye.dev/post/first-post/</guid><description>
&lt;p&gt;不管是下面哪一個待辦事項，都不是突發奇想，而是一直想嘗試的事情：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;架一個個人網站&lt;/li&gt;
&lt;li&gt;寫 blog&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="在這個網站之前我還擁有過的個人頁面有"&gt;在這個網站之前，我還擁有過的個人頁面有：&lt;/h2&gt;
&lt;h3 id="msn-社群"&gt;MSN 社群&lt;/h3&gt;
&lt;p&gt;第一次有個人頁面，是在小學時候的 MSN 社群，當時為了發布 LF2 （小朋友齊打交二）的改檔而申請的，但是後來 MSN 關了之後就沒有然後了，改檔備份也消失。&lt;/p&gt;
&lt;h3 id="無名小站"&gt;無名小站&lt;/h3&gt;
&lt;p&gt;國三因為看大家寫無名小站很好玩（剛好那時候蔚為風潮），所以就跟著創了，但這時候就是屁孩文為主。用現代人的觀點來看，無名小站就是當時學生的 FB。&lt;/p&gt;
&lt;h3 id="痞客邦-pixnet"&gt;痞客邦 PIXNET&lt;/h3&gt;
&lt;p&gt;第一個比較正式的 blog，大學時候創的，主要是放跟家教有關的東西，每次丟家教的履歷的時候都會附上網址，目前感覺效果是不錯。那時候接家教接的很有心得，目前搬過來的文章的主要來源也都是那裡。&lt;/p&gt;
&lt;p&gt;不過大四忙起來之後就比較少經營。&lt;/p&gt;
&lt;h3 id="blogger"&gt;Blogger&lt;/h3&gt;
&lt;p&gt;上面的無名小站倒掉之後，個人 blog 就投靠了 Blogger，畢竟現在是 Google 旗下產品之一，感覺比較不會倒。不過因為充滿了中學時期的黑歷史，目前關起來了。&lt;/p&gt;
&lt;p&gt;另外我也有匿名經營一個歌詞翻譯 blog，但是也很久沒更新了XD。&lt;/p&gt;
&lt;h2 id="所以關於本站"&gt;所以關於本站&lt;/h2&gt;
&lt;p&gt;因為&lt;del&gt;誤入&lt;/del&gt;理論化學組，這幾年把 coding 能力撿回來之外還提升了不少（離 pro 還很遠就是）。此外，在當研究助理的時期也幫實驗室維護網站（雖然更新頻率不太高），我們實驗室的網站是用 WordPress 架在機房裡面的，當時就有種「想要以後也自己來架個站」的想法。&lt;/p&gt;
&lt;p&gt;原本的痞客邦網站，因為寫了一些家教的東西，雖然頻率很低但是偶爾還是會收到家長的留言問問題，我也因此接到家教過，所以 blog 要是經營的起來多少有些幫助的。&lt;/p&gt;
&lt;p&gt;於是決定把大四之後就斷掉的 blog 生涯延續下去並擴展，人要有 &lt;a href="http://www.oneokrock.com/jp/music/1703"&gt;Ambitions&lt;/a&gt;！！&lt;/p&gt;
&lt;h3 id="架在哪"&gt;架在哪&lt;/h3&gt;
&lt;p&gt;網站雖然是放在雲端，但是跟天空中的雲不一樣，還是要有個本體的。如果是公家單位或是公司有個不斷電的機器，那就直接放在那裡就好了，但是一般人很難啊，不管是硬體電費網路費啥的都是一筆支出，所以現在有不少的免費和付費平台給大家架站。雖然很想列個 list，不過感覺列出來就會有「其實還有XXXX喔？」「XXXX其實不是」之類的發言（因為我沒有一個一個試過所以，怕.jpg），加上其實是很好 Google 的資訊&amp;hellip;。&lt;/p&gt;
&lt;p&gt;剛好有 GitHub，也有認識的人架在 GitHub 上，就直接選定了 GitHub Page。&lt;/p&gt;
&lt;h3 id="框架"&gt;框架&lt;/h3&gt;
&lt;p&gt;其實這年頭架站也沒那麼困難的，有很多 framework 可以幫你省下很多事，也有直接 deploy 就有後台 UI 可以登入管理的。&lt;br&gt;
我看到的 blog 框架有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;hexo 聽說是台灣產&lt;/li&gt;
&lt;li&gt;hugo&lt;/li&gt;
&lt;li&gt;jekyll&lt;/li&gt;
&lt;li&gt;Octopress&lt;/li&gt;
&lt;li&gt;Wordpress&lt;/li&gt;
&lt;li&gt;Ghost&lt;/li&gt;
&lt;li&gt;&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最後我是選了 hugo 當作我的框架，去 GitHub 上面 initialize，改一下 config.toml，套用主題 Blackburn（一個日本人寫的主題，看得蠻順眼的），clone 上去網站就這樣蹦出來了。&lt;/p&gt;
&lt;p&gt;&lt;del&gt;生在二十一世紀&lt;/del&gt;真好。&lt;/p&gt;
&lt;h3 id="自動化發布"&gt;自動化發布&lt;/h3&gt;
&lt;p&gt;GitHub 支援 jekyll 直接在一個專案庫部署，不過因為我是用 hugo，你把 data clone 上去，就還是原始碼。這時，常見的招式是開兩個 git 專案，然後只 clone 產生的 html 們。不過這樣蠻累的，如果原始碼也用 git 管理，那每次更變你都要 git commit 兩次，很麻煩，而且還有可能會有不同步的問題，如果你沒管理好的話。&lt;/p&gt;
&lt;p&gt;這個時候就是 Google 大神派上用場的時候啦。&lt;/p&gt;
&lt;p&gt;Wercker 是一個荷蘭出產的自動化測試與部署專案的工具，以我這種背景的人是第一次聽到，但看完大概的介紹後，感覺這東西就是：很酷，很屌，很方便，所以就用了。&lt;/p&gt;
&lt;p&gt;自從用了 Wercker 之後，&lt;del&gt;考試都考一百分&lt;/del&gt;，我只要把原始碼推上 GitHub，Wercker 就會發現你的 code 更新了，自動幫你 build 和 push 到放 html 們的專案庫，這樣我就只需要管理一個專案庫就好，另外一個就自動化處理了。&lt;/p&gt;
&lt;h3 id="小結"&gt;小結&lt;/h3&gt;
&lt;p&gt;從 Hugo 裝完，到我寫了這篇文章，其實也才第三天，所以，架站真的沒那麼難（只是會偶爾鬼打牆）。&lt;/p&gt;
&lt;p&gt;目前這個網站大概就是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;我的電腦裡面有個本地備份&lt;/li&gt;
&lt;li&gt;VSCode 寫文章（in Markdown），&lt;del&gt;VSCode 大法好&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;本地確認看看沒毛病之後，推上去 GitHub&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;當然我也手很癢改了一些東西，像是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;字體&lt;/li&gt;
&lt;li&gt;側邊欄 icon，素材跟原本的 theme 一樣是從 &lt;a href="https://dwye.dev/"&gt;fontawesome&lt;/a&gt; 來的&lt;/li&gt;
&lt;li&gt;加了幾個頁面&lt;/li&gt;
&lt;li&gt;改了一下 index.html 的模板讓首頁看得到 about&lt;/li&gt;
&lt;li&gt;我可以插入 $\mathrm{\LaTeX}$ 欸（用了 &lt;a href="https://dwye.dev/"&gt;MathJax&lt;/a&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="這個網站之後的發展"&gt;這個網站之後的發展&lt;/h2&gt;
&lt;p&gt;雖然網站好像架好了，&lt;del&gt;但是俗話說，「The end is the beginning」&lt;/del&gt;，其實很多事情都是剛開始。原本的文章大部分都是家教向，不過就像上面說的，這幾年開始碰了不少 coding，也想跟著前輩大神們寫些技術性一點的東西，這也是之後更新的方向之一。另外也希望能把自己聽音樂的興趣發揚一下，不用到像 &lt;a href="https://twitter.com/kazbomyi"&gt;KAZBOM&lt;/a&gt; 那麼厲害，但是寫寫字我應該還是做得到&lt;del&gt;吧&lt;/del&gt;。&lt;/p&gt;
&lt;p&gt;雖然有點大方向，但是其實網站的走向也是很隨性的，&lt;strong&gt;我的人生哲學，就是走一步算一步&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id="待辦"&gt;待辦&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;寫文章，更多文章&lt;/li&gt;
&lt;li&gt;中英文語言切換&lt;/li&gt;
&lt;li&gt;去充實人生這樣才能充實這裡（無誤&lt;/li&gt;
&lt;li&gt;&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>回到過去殺了希特勒，那世界會變成怎樣？</title><link>https://dwye.dev/post/kill-hitler/</link><pubDate>Tue, 02 Jan 2018 00:48:45 +0800</pubDate><guid>https://dwye.dev/post/kill-hitler/</guid><description>
&lt;p&gt;寫在最開頭，這只是一篇很歪的廢文。&lt;/p&gt;
&lt;h3 id="正文"&gt;正文&lt;/h3&gt;
&lt;p&gt;這個命題其實不是沒有人問過，也不是沒有人給過一個像樣的答案。有人說，歷史是會修正自己的，如果今天殺掉一個希特勒，那麼一定會有一個類似的人出來取代他，可能德國的法西斯政黨換了名字，卻還是發生了二戰。&lt;/p&gt;
&lt;p&gt;所以如果有人跟我說，要是日本沒有引發珍珠港事件，或是希特勒的部隊沒有在史達林格勒被包圍，甚至什麼中華民國打贏了三大會戰之類的，我都覺得只是會有另外的類似事件，讓歷史往原本的方向去進行。推動歷史的永遠不是一個人、也不是一個事件，而是那個人和那件事件後面的潮流，以及全體人類。&lt;/p&gt;
&lt;p&gt;是的，這個題目就是遊戲『終極動員令：紅色警戒』的劇情：&lt;br&gt;
愛因斯坦發明了時光機，抱著拯救全體人類的心情，回到1924年的慕尼黑，抹殺了剛出獄的希特勒。從遊戲名字就可以猜想之後發生了什麼事了，冷戰時期最能代表紅色的事物不言而喻。&lt;/p&gt;
&lt;p&gt;當初第一次學到二戰附近的歷史覺得很有趣，就像玩過信長的人會知道服部半藏跟本多忠勝一樣，我對近現代歷史的基本認知，就是美國總統杜根，帶領歐洲的同盟國夥伴：英、法、德三國打敗沙皇後代洛馬諾夫引發的蘇聯侵略——這是紅色警戒 2 代的劇情XD，我當然知道這場戰爭沒有發生，後來 1990 年柏林圍牆倒塌，1991 年蘇聯解體獨立國協成立，國小老師叫我背過，所以我還知道這些事，但是對於第一二次世界大戰到底是誰打誰我還真的沒什麼頭緒，只知道美國和同盟國是好人，蘇聯是壞人，而創立蘇聯的史達林是壞人中的壞人。&lt;/p&gt;
&lt;p&gt;所以當我知道其實二戰是我以前認知的同盟國裡面的一國所引起的時候，就覺得充滿衝擊感，就像從小被認為是奢侈品的象徵 iPhone 一樣，原本以為是神到不能再神的劃時代高科技，結果長大之後好不容易有錢換了一隻 iPhone，才發現他連基本的中文輸入法都做不好&amp;hellip;&amp;hellip;離題了。&lt;/p&gt;
&lt;p&gt;更讓人驚訝的是這段歷史真就像在打遊戲破關一樣，玩家選擇德軍，訓練關卡是把部隊開進奧地利，然後第一關是佔領捷克，第二關北上丹麥，第三關確保挪威補給線，第四關華沙攻略，然後荷蘭比利時盧森堡法國，媽的真的跟打遊戲一樣，你說打就真的打下來，怎麼可能真這麼順利？於是就一路進行到海獅作戰、巴巴羅薩行動，最後一關是在華盛頓丟一顆核彈，然後就接上《高堡奇人》（The Man in the High Castle）的世界觀了（誤）。&lt;/p&gt;
&lt;p&gt;史實上是當初被我認為的大壞蛋史達林，和新學到的壞蛋希特勒，兩個人互打起來了。這下有趣了，一般來說看故事都是壞人打好人，好人合作變強反擊一波幹掉壞人，結果現實世界居然有這種壞人互打的劇情。結果兩個獨裁政權互咬互傷，鷸蚌相爭英美得利，才接上我們現代所生存的國際世界。&lt;/p&gt;
&lt;p&gt;而且另外一個史實是，海森堡的團隊沒能夠為德國開發出核彈，反而是被希特勒趕跑的愛因斯坦的團隊先造出來。這件事情成為後來歷史的疑雲之一：海森堡到底是開發不出來？還是就如某些辯護者所說的：他能夠但他蓄意避免了。畢竟如果發狂的希特勒拿到核彈，一定是二話不說先往一個人口多的地方丟下去報復對方——結果反而是終結戰爭的好人方美國做了同樣的事，用核彈把兩個日本大城鎮變成了灰燼。&lt;/p&gt;
&lt;p&gt;這邊也有一個有趣的事：量子力學的創始者掌控不了相對論，相對論的提出者也無法理解量子力學。哥本哈根詮釋用機率論解釋波函數平方的物理意義，愛因斯坦卻質問：上帝怎麼可能丟骰子？他的後繼者霍金回答了：上帝不止丟骰子，還為了搞混我們兒把它扔到你看不見的地方&amp;hellip;&amp;hellip;又離題了。&lt;/p&gt;
&lt;p&gt;當然好人壞人這種東西都是大眾去定義的，而這樣的定義也可以因為每個人的認知而不同，如果沒有凡爾賽會議的割地賠款以及不平等要求，我覺得反而比消滅希特勒更能夠阻止二戰發生。但是前面才說過，凡爾賽會議之所以會這樣規劃，也不是當時座位上的幾個人決定的，當時世界的認知就是那樣，打贏了就是有錢有地有資源，打輸了就下去等著被瓜分。就像打贏二戰的美國，丟了兩顆核彈都能說成是阻止更多人員傷亡，好像砸下去時城市裡面無辜的老人小孩都不是人一樣，還能開心地當他的世界警察。&lt;/p&gt;
&lt;p&gt;紅色警戒告訴我們，即使少了希特勒，史達林還活得好好的，而且被抽去平衡的架空世界，反而更加殘酷。但我覺得，一定會有一個曾經追隨希特勒的人，會取代他的位置，這個人，可能是海德里希，也可能是戈培林或是希姆萊。也許這個新的納粹領袖會是個更聰明的人，少了些領袖魅力，卻多了平穩的思緒，這樣的人反而比一個單純靠勇氣與魄力在打仗的戰爭瘋子還可怕。&lt;/p&gt;
&lt;p&gt;結論就是，還是趕快去充實自己的人生吧，改變世界太難，改變自己比起來容易太多了，一個粒子的系統可以有可逆性，熱力學卻告訴我們不要跟時間的後盾：熵 (entropy) 去做對抗。&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>剛結束當兵前最後一場家教</title><link>https://dwye.dev/post/%E5%89%9B%E7%B5%90%E6%9D%9F%E7%95%B6%E5%85%B5%E5%89%8D%E6%9C%80%E5%BE%8C%E4%B8%80%E5%A0%B4%E5%AE%B6%E6%95%99/</link><pubDate>Tue, 27 Sep 2016 00:36:06 +0800</pubDate><guid>https://dwye.dev/post/%E5%89%9B%E7%B5%90%E6%9D%9F%E7%95%B6%E5%85%B5%E5%89%8D%E6%9C%80%E5%BE%8C%E4%B8%80%E5%A0%B4%E5%AE%B6%E6%95%99/</guid><description>
&lt;p&gt;剛結束當兵前最後一場家教，所以想打點心得，不知道以後還有沒有機會做這份行業，總之一切的開始都是緣份。&lt;/p&gt;
&lt;p&gt;剛上大學總是有很多野心，當時有個願望，就是希望能夠早日經濟獨立，所以第一件事情就是跟家裡要來存摺，自己的錢自己管，然後建立零用錢制，所以有了基本收入。再來就是如何賺錢了。&lt;/p&gt;
&lt;p&gt;除了因為台大學生很多都是用家教當打工，就我自己而言，其實高中平時成績也不是特別好，教高中物理這種事情原本應該是想都想不到的，除了高三上成績不知道為什麼破格的高，不過到高三下又跌回班上十幾名。後來因為不幸要指考，只好把物理乖乖補起來，才發現其實高中物理對我來說還蠻簡單的，當時有自己準備一份公式筆記，以後搞不好有機會拿來幫別人解決物理問題。&lt;/p&gt;
&lt;p&gt;原本只是在 SNS 上半開玩笑性質的幾句話，後來就真的認真的家教起來了。後來因為朋友D先生在補習班工作，也把我拉了過去，慢慢累積實力，加上剛開始每次家教都會花很多小時備課，也就熟能生巧了起來，開始想透過其他平台來找家教。過程當然有順利也有不順利，也有好幾個人是教了半學期一學期就不見人影，畢竟這行業競爭對手真的很多，除了同行、補習班，甚至是學校老師、以及學生自己。如果學生不小心找到了讀書的訣竅，家教老師也就失業了，雖然這是我覺得最好的失業方式，比因為成績沒進步而失業好太多XD。&lt;/p&gt;
&lt;p&gt;一份家教的結束，除了被補習班搶走或被fire，也有可能是學生考完試畢業惹，這種方式結束的還是最多的，而且佔了大部分比例，所以也是值得欣慰的事情吧。不過結束之後又是另一回事，其實還有在聯絡的比例真的很少，搞不好兩年前的學生走在路上我還不會第一時間認出呢。我覺得這種私人性質的工作除了賺錢之外也有交朋友的性質在，在還沒出社會的時候認識越多人也不是一件壞事，只是希望能留下的都是好印象。畢竟身為邊緣人以前實在累積太少朋友了，透過打工來認識人也是一種彌補的管道～以後在社會上有機會的話也能互相幫助。&lt;/p&gt;
&lt;p&gt;這邊突然很想講兩件事情。&lt;/p&gt;
&lt;p&gt;第一件事情就是，如果教的是女生學生，通常跟別人講的時候，對方就會露出一臉你在虧妹子的表情，也有人會嘴叫我把握機會。不過說真的，還真的不會想要ww，不只會覺得怪，多重身份疊上去處理起來真的蠻麻煩的，是那種光是用腦袋想就覺得麻煩，就現實面來講就不會想要，反正機會別的地方也有，不差這一點點。那如果說教女生會不會比教男生爽，可是我覺得平常跟男生朋友嘴炮也很爽，所以如果跟男生的學生很有話聊又很主動學習，其實教起來也蠻開心的，如果女生是不太會自己唸書又不太講話的，教起來也不會說多輕鬆，而且你還要比他積極比他有希望有笑容，心裡面其實蠻累的。&lt;/p&gt;
&lt;p&gt;然後還有關於收錢的部分。雖然一開始只是想賺錢，但後來又覺得好像有賺到錢就好，不用賺那麼多沒關係，畢竟也是自己喜歡的工作，就是不要空虛的度過就好。而且慢慢會意識到自己的責任感，是比賺到多少錢還重要，如果只有拿錢但是學生沒有什麼進步甚至還退步，其實也會良心不安。&lt;/p&gt;
&lt;p&gt;有些時候也會覺得，成績不是一切，其實排名差不多的大學不會差在那幾所之間，重要的是把未來可能用到的主要觀念學好，分數夠了就好不用求很高，可是常常都是家長和學生比我還在意這些，而且由一個台大的人來講這些實在沒什麼說服力XD。&lt;/p&gt;
&lt;p&gt;我覺得每次的相遇都是一次緣份，人生要不要往上爬也是自己的選擇，但是在世間遊走體驗就足夠是一種人生的意義了，大概就是這樣吧。謝謝所以曾經在這方面照顧過我或被我照顧的人。&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>談日本搖滾樂團 ONE OK ROCK 的興起</title><link>https://dwye.dev/post/one-ok-rock/</link><pubDate>Wed, 20 Apr 2016 00:18:03 +0800</pubDate><guid>https://dwye.dev/post/one-ok-rock/</guid><description>
&lt;p&gt;這篇是我在通識課「日本文化史」的期中報告。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="一前言"&gt;一、前言&lt;/h2&gt;
&lt;p&gt;首先於第二部分我會先簡單介紹關於 ONE OK ROCK (以下簡稱ワンオク)的一些基本資訊，接著於第三部分將我認為他們的特色分項討論，最後第四部分會簡述ワンオク對我的影響以及選ワンオク當作主題的理由，並於第五部分做總結。&lt;/p&gt;
&lt;h2 id="二樂團簡介"&gt;二、樂團簡介&lt;/h2&gt;
&lt;p&gt;ワンオク目前是一個四人樂團，於 2006 年成立，已有十年歷史，團員為主唱 Taka，吉 他手 Toru，Bass 手 Ryota 以及鼓手 Tomoya，目前發有七張正規專輯以及九張單曲。團名的由來是因為日本的「L」和「R」發音相同，從他們以前學生時代的練團時間半夜的「one o’clock」音轉而來，兼具他們搖滾為本位風格的意思。光是從半夜一點也要練習，就可以看出他們對音樂的熱忱。&lt;/p&gt;
&lt;p&gt;ワンオク的樂團路並非一路順遂，曾經因為主吉他手 Alex 捲入性騷擾事件被逮捕，而導致樂團活動被迫中止，一瞬間演唱會、電視節目主題曲、待發行單曲什麼的全部都沒了，可以說是樂團歷史的谷底。然而團員憑著不能半途而廢的精神，以及對音樂的熱忱，就算少一個人也要走下去。他們發行了《完全感覚 Dreamer》單曲後再度一炮走紅，這首歌對他們的意義重大，是直到現在 6 年後這首歌都是 live 一定會表演的最後一首歌。&lt;/p&gt;
&lt;p&gt;樂團風格一開始充滿了反骨、叛逆的龐克風，可以從《Keep it real》中「くだらない前評判と大人達の世間体」別去在意那些成人的現實觀與批評、《内秘心書》中「ツメを伸ばしたウソ書き人，こんな時代だから」對世界的不滿，以及《カラス》中「泣いてるか笑ってるか，ソレすら誰にも分かりはしない」的無奈怨恨看出。後來也漸漸出現一些正向的歌，如《キミシダイ列車》中「過去の自分が今僕の土台となる」的激勵，以及紀念日本 311 大地震的《Be the light》中「Tomorrow’s night returns to light」的正向呼喊。也有正面的告白情歌《Wherever you are》的「Wherever you are, I’ll always make you smile」之類。風格逐漸多樣化，但最重要的搖滾核心，仍是他們的主要路線。&lt;/p&gt;
&lt;p&gt;近年來ワンオク積極走向國際化，靠著為「神劍闖江湖」電影三部曲譜寫主題曲，並大量增加英文歌詞使用量以及大量的海外巡迴來腳踏實地推廣知名度，並加盟國際音樂公司華納兄弟音樂，發行全球版專輯，走向世界。&lt;/p&gt;
&lt;h2 id="三特色分析"&gt;三、特色分析&lt;/h2&gt;
&lt;h3 id="積極追求國際化"&gt;積極追求國際化&lt;/h3&gt;
&lt;p&gt;因為日本注重著作權，有的甚至連 MV 都只放片段到 Youtube，想看完整版必須買單曲或專輯，這方面和其他國家完全不同。為了避免海外盜版，不少音樂甚至會封鎖海外 ip，想 要看他們的影片必須要使用 VPN 跳板到日本。然而ワンオク在這方面卻完全與之相反，他們和歐美樂團一樣，上傳大量 MV 和 live 表演影片來進行網路宣傳，在海外表演也尊重當地錄音錄影文化，和日本國內對版權的一板一眼不同。&lt;/p&gt;
&lt;p&gt;除此之外，從第一張專輯時，他們就採用了英日歌詞混合的創作模式，到後來更變本加厲，英文歌詞比重越來越重，也有好幾首歌只有英文歌詞，如《Reflection》、《Never Let This Go》&amp;hellip; 等。主唱 Taka 有著想強烈走向世界的夢想，也所以近年和美國公司簽約，發行全英文版本專輯《35xxxv Deluxe Edition》，甚至發生過將原本寫好的日文歌詞砍掉用英文重寫，就為了將自己的音樂讓更多人理解。&lt;/p&gt;
&lt;h3 id="簡單卻直白的歌詞"&gt;簡單卻直白的歌詞&lt;/h3&gt;
&lt;p&gt;ワンオク的歌詞不走文謅謅的路線，像是《エトセトラ》中「分かってるんだ，戻んないのは，でもでも&amp;hellip;」這樣如同說話一般的歌詞，彷彿真的是失戀時的自言自語。又或是《Deeper Deeper》中直接點出「いつだって人は迷うんだって」，直白的歌詞卻能夠引人共鳴，連非日本母語的人只要學過一點點日文就可以理解歌詞，或是英文不好的人也可以簡單理解他們英文部分的歌詞，這也是我認為他們能把自己推向世界的原因之一。&lt;/p&gt;
&lt;h3 id="抒情和搖滾都能入味"&gt;抒情和搖滾都能入味&lt;/h3&gt;
&lt;p&gt;如同第二段說到，ワンオク嘗試過各種不同的風格，從一開始的龐克、還有 rap 到後來加入一些電子元素。當我同學興致盎然的對我推薦ワンオク的時候，他說了他喜歡他們的理由就是「不管是抒情或搖滾都能寫得很好」。例如說在台灣的 KKBOX 日文歌排行榜，抒情歌《Wherever you are》一直都佔領著前三名的位置。同時身為搖滾樂團的他們，如同《No Scared》般的嘶吼，《The Beginning》的堅定，《Liar》的憤怒，主唱 Taka 強大的音域與爆發能力以及辨認度十足的嗓音，是目前樂團界中少有的，不需要依靠金屬腔也能讓人感到十分的爆發力。Tomoya 的強大鼓功，多變華麗而聽起來過癮的打法，也為樂團的搖滾加了不少分數。&lt;/p&gt;
&lt;h3 id="強大的演唱會現場實力以及渲染力"&gt;強大的演唱會現場實力以及渲染力&lt;/h3&gt;
&lt;p&gt;我認為ワンオク最強大的地方，莫過於他們現場表演的實力以及渲染力。搖滾樂團不能只是錄音室歌手，不能像韓國團體一樣彈空氣吉他，自己寫出來的歌，在現場表演時不能感冒唱不上去，鼓手吉他手不能跟不上搖滾的快速節拍。除此之外，現場更要擁有足夠的體力跳跳唱唱，陪歌迷搖滾將近三個小時，年輕充滿活力的ワンオク正把現場發揮得淋漓盡致，不少國外網友都對於主唱 Taka 邊跳還能邊爆發高音的實力感到佩服而一聽成主顧。他們也善用他們的現場實力，不常上節目，反而時時都在巡迴表演，直接用現場表演拉攏更多歌迷。&lt;/p&gt;
&lt;h2 id="四one-ok-rock-與我"&gt;四、ONE OK ROCK 與我&lt;/h2&gt;
&lt;p&gt;之所以選擇ワンオク當作主題，因為他們是我對於國外音樂的啟蒙團，讓我打開音樂的視野，接觸流行音樂以外的音樂。後來也因此慢慢能接受不同種類的音樂風格，知道有流行音樂以外的世界，也知道除了主唱之外，樂器的靈魂也是很重要的。因此慢慢聽了以前認為不知道在唱什麼的閃靈樂團，能夠關注地下認真彈奏每個音符的獨立音樂圈，連結到歐美日各國厲害的搖滾樂團，開始思考每個樂團的主軸以及他們想要傳達給世界什麼。&lt;/p&gt;
&lt;p&gt;ワンオク的很多首歌陪我走過了人生的各個感動時刻，在此不多贅述，而我也會購買正版專輯對音樂做最好的支持，並參加台灣粉絲專頁的應援活動以及在 PTT 上參與討論，也參與維基百科的條目編寫，讓好奇他們的人能夠得到更多關於他們的訊息。&lt;/p&gt;
&lt;h2 id="五結語"&gt;五、結語&lt;/h2&gt;
&lt;p&gt;ワンオク是目前在日本國內外都有一定知名度的樂團，但是在日本國內仍然與一些流行天團差得遠，也許搖滾音樂的愛好者本來就不是佔多數吧？然而他們在國外的知名度，絕對是目前日本樂團首屈一指的。以台灣來說，他們是目前唯一一組在台北小巨蛋開過演唱會的日本樂團，而且還一開就是兩天。&lt;/p&gt;
&lt;p&gt;ワンオク是個搖滾樂團，而且是喜歡嘗試多變曲風的搖滾樂團，Taka 曾在訪談說，雖然說想要有更多歌迷，但他們不會擔心風格的轉變造成歌迷流失，因為他們想要傳達的思想就是「做自己就對了，然後把自己的這樣子傳達給更多人」，不管是透過搖滾的宣洩也好，透過激勵的旋律也好，希望能夠帶給世界正面的力量。ワンオク才剛正式邁向全球音樂圈，下一張專輯將往什麼風格發展，未來都還尚未明朗，但也希望他們能夠不忘初衷，繼續向歌迷傳達他們正向的搖滾音樂與思想。&lt;/p&gt;
&lt;h2 id="六參考資料"&gt;六、參考資料&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="//zh.wikipedia.org/wiki/ONE_OK_ROCK"&gt;中文/英文/日文維基百科 ONE OK ROCK 以及相關條目&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="//www.jame-world.com/uk/articles-4982-interview-with-one-ok-rock-in-tokyo.html"&gt;Interview with ONE OK ROCK in Tokyo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="//www.kkbox.com/tw/tc/column/interviews-46-763-1.html"&gt;ONE OK ROCK的決意:「目標世界第一!!」&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ptt.cc/bbs/ONE_OK_ROCK/M.1354658668.A.C7A.html"&gt;ROCKIN’ON JAPAN 2012 年 6 月號 TAKA 篇, PTT ONE_OK_ROCK 版&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>[現場] 我們囚禁在格林威治的規則裡</title><link>https://dwye.dev/post/sodagreen/</link><pubDate>Tue, 15 Dec 2015 01:26:27 +0800</pubDate><guid>https://dwye.dev/post/sodagreen/</guid><description>
&lt;p&gt;第一次的自己買票的小巨蛋，獻給蘇打綠。&lt;/p&gt;
&lt;p&gt;第一次的蘇打綠，獻給《故事未了》。&lt;/p&gt;
&lt;h2 id="強盜就躲在城堡"&gt;強盜就躲在城堡&lt;/h2&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;正好我的日本同學很會唱歌，把這首當年的新歌裡所描述的不滿、反抗、指控等詮釋的很完美。在他的強力推廣下，從此之後我就因緣際會地迷上蘇打綠，慢慢自己找更多首歌來聽，《這天》、《近未來》、《小宇宙》、《相信》&amp;hellip;&amp;hellip;等歌陪我度過不少快樂、孤單、喜悅與失望。從《你在煩惱什麼》開始，每一張專輯都擺在我的書櫃上，與《蘇打誌》一起。&lt;/p&gt;
&lt;p&gt;第一場的蘇打綠演唱會，不知怎麼地成為很重要的人生目標之一，想要早點看到，卻又希望能一直把期待留著。&lt;/p&gt;
&lt;p&gt;原本因為手邊的票轉讓不出，差點就決定兩張一起賣掉，還好最後一刻找到了願意跟我換票的人，以及願意收購另一張票的人，讓我懷著複雜的心情（既期待又害怕過度期待XD），迎接人生第一場正式的蘇打綠。&lt;/p&gt;
&lt;h2 id="人生一場大夢葉落知多少"&gt;人生一場大夢，葉落知多少&lt;/h2&gt;
&lt;p&gt;紫2E的座位比我想像來得近，小威的鼓在眼前佔了一定比例，突然覺得其實小巨蛋也不大嘛。開場的中央舞台升起，樂手、指揮、團員們相繼走出，讓我想起小時候的國樂表演，彷彿角色對調，而一場盛大正式的音樂會正要在此起彼落的掌聲中展開。&lt;/p&gt;
&lt;p&gt;老實說，聽朋友說小巨蛋的音場對古典音樂來說很不好，讓我一開始有點忐忑不安。當《故事》拉開了序幕，比原曲更加壯闊的伴奏群起奔馳，也許是我的耳朵還沒習慣，也或許是太專注空氣裡每個小雜音，除了青峰的歌聲較清晰外，回音與樂器互相干擾，實在聽不太清楚細節，比起主唱的旋律我更喜歡專注於音樂的細節安排，所以一開始有點小失望，直到&amp;hellip;&amp;hellip;&lt;/p&gt;
&lt;h2 id="夏蟬猛把天地叫窄容不下過去未來"&gt;夏蟬猛把天地叫窄，容不下過去未來&lt;/h2&gt;
&lt;p&gt;前奏我還在猜是不是要翻唱誰的歌或演奏誰的作品，和弦越聽越耳熟，直到「某個夏天一座花園」就立刻認出了這首！！《蟬想》一直是我很喜歡的作品，詞意不說，單說轉調就讓人入迷，我在現場也聽得入迷了，上一首歌出現的擔心完全消失！果然還是要一心一意沈浸於音樂中才享受！說真的這首的出現讓我十分驚喜！&lt;/p&gt;
&lt;h2 id="放開時間空間而存在"&gt;放開時間空間而存在&lt;/h2&gt;
&lt;p&gt;《融雪之前》，一首寧靜的歌，自我思想著哲理，配上流暢的木吉他，是我之前對這首歌的印象。配上交響樂團後，額外的優美。原本我是喜歡搖滾樂的，這次的表演中卻發現許多不太搖滾的慢歌都被詮釋的極佳，讓我有機會用新的角度欣賞它們。&lt;/p&gt;
&lt;p&gt;秋、夏、春，下首就是冬了吧！&lt;/p&gt;
&lt;h2 id="只是要比誰病的輕"&gt;只是要比，誰病的輕&lt;/h2&gt;
&lt;p&gt;「《冬　未了》專輯開門見山，即帶來完整的樂團大合奏，所有的樂器全用上&amp;hellip;&amp;hellip;」，聽到開場的樂團大合奏，就讓我想起《蘇打誌》中的阿龔的這句話，這就是《痛快的哀艷》了吧！交響樂團開始展現他們的實力了，這首歌標誌著「冬」的饗宴真正開始了。附帶一提，在我位置正前方的小洋老師彈的好激動好入迷，好愛他哈哈，他扛了不少重要的電吉他旋律，是我覺得加的非常適當非常美的。&lt;/p&gt;
&lt;h2 id="這曠古的最仇恨之歌"&gt;這曠古的最仇恨之歌&lt;/h2&gt;
&lt;p&gt;一首寫希特勒的歌，特別吸引著我。時間回到1945年，紅軍坦克輾過柏林廢墟，地下碉堡平靜而詭譎，元首出了碉堡就再也不是元首了。蘇打綠穿越時空的質問，元首或瘋狂、或沈默，最後還是做出一樣的選擇。祂按下了最後的開關，同時啟動了婚禮與喪禮，在小威的鼓聲與交響的奔騰中燃燒自我，寫下這曠古的最毀滅之歌，讓整個德意志與祂一同墮入深層地獄。&lt;/p&gt;
&lt;p&gt;（PS. 明天的軍訓報告正好要講第三帝國XD）&lt;/p&gt;
&lt;h2 id="雖有陰晴-你的手卻透明而清晰"&gt;雖有陰晴 你的手卻透明而清晰&lt;/h2&gt;
&lt;p&gt;真的是照著專輯曲目來呀，不過也代表著原本的專輯曲序設計正好符合音樂會的情緒起伏！一首沈重的歌，接下來是《地平線》陪著我們，毀滅之後重現生機，不管怎麼樣日出都陪著我們！整首歌我最專注的是最後一次副歌「充滿了喜悅，口滿了稱謝」的鼓，很喜歡專輯中那段的混音，小威也沒有讓我失望打得很用力啊！（糟糕我怎麼一直在聽鼓）&lt;/p&gt;
&lt;h2 id="曾經給你太多傷心過過後總會寬闊"&gt;曾經給你太多，傷心過，過後總會寬闊&lt;/h2&gt;
&lt;p&gt;居然能聽到四季以外的歌，讓我有點出乎意料哈哈。不過可惜這邊好像是讓交響樂休息，但是光是原本的版本就夠催人熱淚的。&lt;/p&gt;
&lt;p&gt;「請你張開雙手，讓我死在懷中。」即使逐漸忘了有多久，腦海中浮現著相同的面孔。&lt;/p&gt;
&lt;p&gt;在交往後，我的目標是想變得富有；分手之後，卻想要繼續升學單純地走學術的路。不同的心境有不同的野心，但不論是在一起還是離別都會改變一個人呢。第一次在分手之後好好聽這首歌，真的是聽完整首要偷偷拭一下眼淚。除了專注於歌詞中，看著青峰繞圓形舞台轉圈圈也是某種樂趣XD。&lt;/p&gt;
&lt;p&gt;抱歉打了點自己的事情。&lt;/p&gt;
&lt;h2 id="藥水請蕭邦地擦謊言請李白地講"&gt;藥水請蕭邦地擦，謊言請李白地講&lt;/h2&gt;
&lt;p&gt;回到秋天了，這似乎是C大很喜歡的歌～有點傷心之後的療癒感。我一直在想蕭邦地擦是怎麼樣地擦，整個人就失焦了sorry XDDD&lt;/p&gt;
&lt;p&gt;Talking其實不怎麼出乎意料，我覺得C大一定會忍不住想講講話哈哈，簡單的問候同時問我們喜不喜歡，當然是超！喜！歡！！！接下來是我覺得最驚喜的part了！&lt;/p&gt;
&lt;h2 id="於是你哭泣於是你孤寂停在那不能自己的一瞬間"&gt;於是你哭泣，於是你孤寂，停在那不能自己的一瞬間&lt;/h2&gt;
&lt;p&gt;天啊是第一張專輯第一首歌！！！我看了看周圍好像只有我和我旁邊的兩個女生聽過哈哈，原來我也是鐵粉啊知道打綠有發行的每一首歌！還是我在第一張專輯裡面很喜歡的一首！能夠聽到交響樂版嚇死人了！還好有來聽不然真的要《後悔莫及》啦！&lt;/p&gt;
&lt;h2 id="你想找我想逃倒不如分道揚鑣"&gt;你想找、我想逃，倒不如分道揚鑣&lt;/h2&gt;
&lt;p&gt;這首歌居然叫大家動起來是想怎樣！害我在青峰開口才猜出是這首XDDD，這首歌算是我比較晚認識的一首，可是認識之後卻不知道為什麼很喜歡哈哈，大概是身邊也有甩也甩不掉的討厭鬼吧（噓，小聲點）。跟剛剛一樣我周圍只有幾個人知道這首，當然就不客氣地大聲唱啦～整場演唱會難得可以大方的活動筋骨！&lt;/p&gt;
&lt;p&gt;連指揮和樂團都開始手舞足蹈，現場一片歡樂真的蠻喜歡這種感覺的。&lt;/p&gt;
&lt;h2 id="從繽紛到昨天凌晨蒸散夢才能永恆"&gt;從繽紛到昨天凌晨，蒸散夢才能永恆&lt;/h2&gt;
&lt;p&gt;冬專輯的最愛！！天啊從一首擺來擺去的歌接道一首有人自殺的歌，叫人怎麼冷靜下來啊！喜歡標題這句歌詞時青峰唱起來的流暢感，聽了幾百遍仍然著迷。第一次的電吉他間奏也很喜歡，還有專輯裡面混音很奇幻的bridge配上好聽的鼓，現場我都有特別注意聽這些點！打綠再次證明他們不是錄音室樂團！能夠把這首在現場表演得比專輯更出色！希望趕快出MV這樣才可以在FB洗版分享XDDD&lt;/p&gt;
&lt;h2 id="不過是一尊藝術的門外漢"&gt;不過是一尊藝術的門外漢&lt;/h2&gt;
&lt;p&gt;安靜的一首歌，好像想著自己卻又說著誰，現場一樣意外地比專輯令人著迷。其實我比較喜歡樂團（鼓、Bass等）進來之後的部分。&lt;/p&gt;
&lt;h2 id="那等不到的道歉交給風"&gt;那等不到的道歉，交給風&lt;/h2&gt;
&lt;p&gt;「邊界上人的思念」的現場版！在文明之下，令人反思的一種意境，而下首歌承接著，覺得真是絕妙的安排。青峰最後的連續幾聲拍子上的嘆息好迷人XD&lt;/p&gt;
&lt;h2 id="這座城市一般享受著奢侈卻莫名失落"&gt;這座城市一般，享受著奢侈卻莫名失落&lt;/h2&gt;
&lt;p&gt;等這首歌等很久了！！「比較龐大的管樂組合僅出現在《城市》當中」《蘇打誌》早就提到了這首，果然真的出現了，正宗的交響樂版！而且是以一種絕妙的意境連接，以及無比滂礡的氣勢堆疊！一層一層的管弦樂把情緒往上帶，好像人類的城市發展一般，人口日益擁擠，數位的浪花讓人喘不過氣。讓人整個聽得過癮，每次聽都忘記哪邊才是副歌，其實整首歌是一個流暢的大組合啊！聽完這首還餘音不絕於耳，出去上廁所還一邊哼XD，覺得如果在這邊結束了也值得了（欸）。拜託發DVD吧我還想再聽一遍！！！！&lt;/p&gt;
&lt;h2 id="其實還有下半場就留給下半篇吧"&gt;其實還有下半場，就留給下半篇吧！&lt;/h2&gt;
&lt;p&gt;附帶一提，報時的設計每次都不一樣是很棒的巧思哈哈，好像是指揮吧用不太標準的中文說「十五分鐘」格外逗趣～&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>物理解題經驗分享：四大步驟</title><link>https://dwye.dev/post/20151028_162837/</link><pubDate>Wed, 28 Oct 2015 16:28:37 +0800</pubDate><guid>https://dwye.dev/post/20151028_162837/</guid><description>
&lt;h2 id="四大步驟"&gt;四大步驟&lt;/h2&gt;
&lt;p&gt;看到題目不知道要做什麼？一個不熟悉的題型應該從何下手？就算是身經百戰的考生，可能今年大考又有新的題型。除了基本題型熟練之外，更重要的是訓練自己不害怕新的題型，能夠運用所學穩健地破解題目。就我觀察，一般解題的高手都有一個自己習慣的解題步驟，可能是自然而然產生的，也可能是自己制定下來自己習慣的。因此在這邊將自己寫物理時候的習慣步驟整理出來分享給大家：&lt;/p&gt;
&lt;h3 id="第一步破題"&gt;第一步：破題&lt;/h3&gt;
&lt;p&gt;找到題目的關鍵字，重量多少？接觸面光滑與否？仰角多少？要不要考慮重力？有沒有固定系統壓力？是圓周運動還是直線飛出？要問什麼？把有用的資訊圈起來，去蕪存菁，看這題到底要考我們什麼。有沒有可以直接刪掉或是直接回答的選項？&lt;/p&gt;
&lt;h3 id="第二步前置準備"&gt;第二步：前置準備&lt;/h3&gt;
&lt;p&gt;畫圖！找到一個可以計算的空間，用自己能理解的方式畫出題目圖像，像是情境圖、力圖，並且把速度、加速度、能量之類的資訊標上去。我們在第一步找的的線索裡面有什麼可以用？如果沒有的，就都設未知數。題目給的資訊，單位是不是都正確？除非答案有特殊要求，不然最好是都換成SI制。若是到這邊沒有什麼想法，就趕快跳下一題做，把這題留到後面再寫，考試中時間是很寶貴的。&lt;/p&gt;
&lt;h3 id="第三步列式"&gt;第三步：列式&lt;/h3&gt;
&lt;p&gt;想想自己學過的物理定律或公式，哪些包含了題目給的條件，哪些可以求出我們要的答案？幾個未知數就要幾條方程式來解。&lt;/p&gt;
&lt;h3 id="第四步計算"&gt;第四步：計算&lt;/h3&gt;
&lt;p&gt;代入消去，相除消去，把未知數求出來，這是所有步驟裡面最簡單的部分。&lt;/p&gt;
&lt;h2 id="應用"&gt;應用&lt;/h2&gt;
&lt;p&gt;大部分題目我都是用上面四步去想的，可能只講理論有點抽象，這邊直接用一道題目當例子好了：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lh4.googleusercontent.com/nHdWd0oiy2VxBZ4tOtSclmPORgUYdTKjotBkUs-2w6YXUaMDBB_nwuv-onC2ySRN1LdQo0D9IGLz0tHz6BUqSQg_PofA0EK1KA7Om7FkX0UBFzP_hJoAursJxeGBs9yXvSGYj0Q4" alt="Physic Quiz"&gt;&lt;/p&gt;
&lt;h3 id="一破題"&gt;一、破題：&lt;/h3&gt;
&lt;p&gt;看圖就知道這是一題斜拋了。&lt;/p&gt;
&lt;p&gt;題目給的資訊有：質量、初速、仰角、最高高度H、A點高度h，重力加速度g。&lt;/p&gt;
&lt;p&gt;題目要問的有：在A點的鉛直速率、OA時間、最高點B時速度跟加速度的關係&lt;/p&gt;
&lt;p&gt;這個時候會發現，(D)選項為純粹考觀念的選項，如果知道最高點B只剩水平速度，而加速度是重力加速度一直都是往下，就可以知道在B點時速度一定會垂直加速度，(D)要選。&lt;/p&gt;
&lt;h3 id="二準備"&gt;二、準備&lt;/h3&gt;
&lt;p&gt;題目已經給圖了，不過我還是畫了幾張圖補助：&lt;/p&gt;
&lt;p&gt;起始速度：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lh6.googleusercontent.com/i5bESwWzUlelnR-mEfltCmL3eaXBvM7JunKR9Kjx8MEYUm_iSMrUueKk7Zdo3qSI9P4wdXtV_ZXIXwSXTYeysuL9oIoZ_MOecNn5fR-PVlqHvRabvp1nKTqOj49MgjII7nFLsj6r" alt="initial velocity"&gt;&lt;/p&gt;
&lt;p&gt;B點速度：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lh6.googleusercontent.com/INdPEsdXYbyiiGRwBa-_kwQnWg7Ofj6QkqCnpr8NLg5_s3MjycwmHOgW2hO-XoiNXQB2URYqdm1fxT2vuKNz5yUNoEq9_nqiR6wOl5zMc5_MKBe0df4POkeKG0P9qY2JQpPsjTwx" alt="velocity of b"&gt;&lt;/p&gt;
&lt;p&gt;A點速度：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://lh6.googleusercontent.com/bMDZ7C3lptDek3b1k5zqYbmnrd4PoJEv4J14v-d7riYFBF91m_OYWCFvaqV3VohyFSZhH3O4vBT3FKBBu05PIqdOKoeqKvqdhTTE00mnQqg5fsN8iTK9gN0SHm24wojDFvtNI2A1" alt="velocity of a"&gt;&lt;/p&gt;
&lt;p&gt;這樣對於速度的概念會比較清楚，重點就是，水平方向是等速度運動，而鉛直方向則是加速度g往下的等加速度運動。&lt;/p&gt;
&lt;h3 id="三列式"&gt;三、列式&lt;/h3&gt;
&lt;p&gt;所以要求出A的鉛直速率，可以善用B到A的等加速度運動去求，既然有了初速0，加速度g，以及兩者高度差（位移）H-h，就可以利用等加速度運動的第三條公式：&lt;/p&gt;
&lt;p&gt;$$ v^2 = v_0^2 + 2a\Delta x $$&lt;/p&gt;
&lt;p&gt;代入之後完成列式。&lt;/p&gt;
&lt;h3 id="四計算"&gt;四、計算&lt;/h3&gt;
&lt;p&gt;在此不詳述，可以算出A點鉛直速率為 $\sqrt{2g(H-h)}$，選項(A)為錯誤&lt;/p&gt;
&lt;p&gt;這時發現，選項(B)和(C)問的是同樣的事情，要問O點到A點的時間，這時既然我們已經對鉛直方向瞭若指掌，何不從鉛直方向下手？重複第三步列式，找到可以求出時間的公式：&lt;/p&gt;
&lt;p&gt;$$ v = v_0 + at $$&lt;/p&gt;
&lt;p&gt;代入已知的O點初始速度、A點的末速以及加速度g，完成列式，注意正負號喔，這邊統一以向上為正：&lt;/p&gt;
&lt;p&gt;$$ - \sqrt{2g(H-h)} = v_0 \sin{\theta} - gt $$&lt;/p&gt;
&lt;p&gt;接者就移項把答案算出來就解決這題了！(C)也要選，所以答案是(C)(D)&lt;/p&gt;
&lt;h2 id="還有"&gt;還有&lt;/h2&gt;
&lt;h3 id="把所有題目當作計算題"&gt;把所有題目當作計算題&lt;/h3&gt;
&lt;p&gt;還有一個很重要的原則，如果是自然組，我建議這個習慣必須培養起來：寫題目的時候，把所有題目當作計算題來寫，把算式寫整齊，寫錯直接擦掉而不要在上方塗改。因果來由交代一下，讓以後的自己也能看得懂，也方便之後回來檢查。這四大步驟，每一步都要動筆，第一步破題時就要在題目上標出關鍵字和有用資訊，畫圖更不能偷懶，有圖可以幫助自己理解題目情境，並且思考題目要怎麼做。&lt;/p&gt;
&lt;h3 id="學習解題"&gt;學習解題&lt;/h3&gt;
&lt;p&gt;步驟是活的，不是每一題都要四步，也有在第四步失敗了回到第一步檢查。若是初學者或是對物理比較沒有感覺的人，很容易一開始就卡住，這時候唯一的訣竅就是「練習」，以戰養戰。我建議能夠模仿老師上課講的例題，看著例題依樣畫葫蘆做類題，或是把例題蓋住重新做一遍，就算把解題方法背下來也沒關係，常有人說不能死背題目，但把一些基本做法背下來我認為是邁向熟練的第一步。&lt;/p&gt;
&lt;p&gt;等到有一定的熟練度後，開始找到自己統一的步驟，找到自己適合的解題方法，可以試著挑戰新題目和較難的題目，實際上很多難題都是由各種基本觀念堆砌起來的。到了更高竿的階段，就是老師常說的「先看到結果再計算」，畫完圖整理完資訊之後，在腦袋裡想出怎麼計算出答案之後才繼續動筆，一次解決。以我個人的經驗，能夠對於每個題目有自己的步驟，是段考考好所必需的，畢竟段考就是同一章節的題目不斷變化。但是指考物理若要考好，則必須能夠對物理熟練到「先看到結果再計算」的境界。&lt;/p&gt;
&lt;h2 id="寫在文章最後隱藏的第五步檢查"&gt;寫在文章最後，隱藏的第五步：檢查。&lt;/h2&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;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>五月天 - 女也 個人小心得</title><link>https://dwye.dev/post/20150721-herstory-with-mayday/</link><pubDate>Tue, 21 Jul 2015 16:28:37 +0800</pubDate><guid>https://dwye.dev/post/20150721-herstory-with-mayday/</guid><description>
&lt;p&gt;忍到專輯出來之後一次用KKBOX聽完&lt;br&gt;
之後邊聽第二遍編寫感想&lt;br&gt;
算是個人小心得啦&amp;hellip;也不是什麼專業的音樂人&lt;br&gt;
另外我對人聲比較沒什麼感覺所以會比較著重於編曲&lt;br&gt;
然後我是理組的沒啥文筆就請見諒喔&lt;/p&gt;
&lt;hr&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/xy-BqLWpoQM?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;生命有一種絕對&lt;/p&gt;
&lt;p&gt;想要傳達的意境跟原本是差不多的&lt;br&gt;
人聲的部分有很多微妙的調整還蠻有意思的&lt;br&gt;
倒數第二次副歌後面進入整首歌的高潮的地方很喜歡&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/Eso_PkuYlB4?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;盛夏光年&lt;/p&gt;
&lt;p&gt;風格變得很迷幻XD&lt;br&gt;
我要我瘋我要我愛那邊有左右聲道的特效&lt;br&gt;
變得比較像是在你耳邊說話的感覺&lt;br&gt;
而不是原本的炸裂感&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/hwe411J3Hbc?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;離表&lt;/p&gt;
&lt;p&gt;從搖滾曲變成電音舞曲，整體還不錯啦&lt;br&gt;
另外很喜歡副歌多出來的合音&lt;br&gt;
（撲通撲通的狂跳）&lt;br&gt;
應該是一首搖得起來的歌，但是可能還是沒辦法像原曲跳起來XD&lt;br&gt;
節奏有點單調，原本的鼓變化其實還蠻用心的有點可惜&lt;br&gt;
不過會這麼嚴苛也是因為這首原本是我最喜歡的歌&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/IBT0_3QUy98?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;突然好想你&lt;/p&gt;
&lt;p&gt;空氣真的好安靜&amp;hellip;.&lt;br&gt;
配樂營造了一個適合哭泣的環境&lt;br&gt;
整體的輕重走向跟原本一樣&lt;br&gt;
「最痛的紀念品」後面多一段副歌被嚇到了XDD&lt;br&gt;
間奏覺得普普就是，只是換個樂器&amp;hellip;沒什麼共鳴&lt;br&gt;
喜歡主歌勝過於副歌&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/eZxkpvoJI2Q?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;溫柔&lt;/p&gt;
&lt;p&gt;變得&amp;hellip;好有節奏感XDDDD&lt;br&gt;
完全就是一首改編歌的Fu（慢慢的人聲配上硬是要節奏感的拍子）&lt;br&gt;
間奏沒什麼變就是&lt;br&gt;
最後一段主歌回到原點的感覺有做出來&lt;br&gt;
但是尾奏突然跑進來有點嚇到XD&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/6dwbkrAKpvY?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;你不是真正的快樂&lt;/p&gt;
&lt;p&gt;雖然是改編版但是不知道為什麼很有原版的感覺XD&lt;br&gt;
只是&amp;hellip;副歌的弦樂完全跟著人聲好像伴唱帶喔XDDD&lt;br&gt;
不是應該要幫人聲合音或作伴奏之類的&lt;br&gt;
「然後才後悔著」後面的Bridge大好！&lt;br&gt;
爆發力也很夠&lt;br&gt;
只是後面突變的很嗨就不像原本要傳達的意思了&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/0sDtXpMUKAE?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;我不願讓你一個人&lt;/p&gt;
&lt;p&gt;第一個感覺是「拍子好慢喔」XDDDD&lt;br&gt;
鋼琴的伴奏有點逗趣，反而沒有那麼多悲傷的感覺&lt;br&gt;
但是總覺得有點扭曲，有種思念對方思念到不知道自己在幹嘛的Fu&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/6n8AitBL7xc?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;如煙&lt;/p&gt;
&lt;p&gt;一開始的彈奏有點小叮噹的感覺啊XDDD&lt;br&gt;
整體編曲很合口味，只是人聲太重就是了&lt;br&gt;
「有沒有」的唱法跟原曲不同&lt;br&gt;
Bridge不像原曲有壓下去的感覺，反而特別突出&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/45mI3RaNOow?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;愛情萬歲&lt;/p&gt;
&lt;p&gt;應該是改最多的一首吧&amp;hellip;&lt;br&gt;
原本的間奏和配樂和節拍全部被吃掉重寫&lt;br&gt;
除了人聲旋律和原本比較像之外幾乎都改了&lt;br&gt;
然後合成音用的超級迷幻，搭上歌詞就變很撫媚了&lt;br&gt;
整首歌的風格十分明顯&lt;br&gt;
雖然不是我會收的風格，但在心中的評價很高（糟了，好像在發好人卡）&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/3Q__2oA69PM?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;我心中尚未崩壞的地方&lt;/p&gt;
&lt;p&gt;這首歌被改得一點都不孤獨啊XDDDD&lt;br&gt;
反而變得很熱鬧的感覺&lt;br&gt;
只聽旋律好像還不錯只是我覺得很不搭啦&lt;br&gt;
個人不是很喜歡&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;整個聽下來最喜歡的前三名分別是&lt;br&gt;
你不是真正的快樂 &amp;gt; 突然好想你 &amp;gt; 離開地球表面&lt;br&gt;
另外整張專輯風格跟五月天差好多喔&lt;br&gt;
聽習慣的人(就我啦)來聽這張反而會一直覺得很怪&lt;br&gt;
如果說有愛再來聽&amp;hellip;.好像也不會因為我會繼續聽五月天的版本XDDD&lt;br&gt;
沒辦法誰叫有愛是對五月天有愛而不是對改編版有愛呢哈哈&lt;br&gt;
不過應該可以把五月天的歌推廣給新的族群吧（不喜歡樂團的XD）&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>高中基礎物理補充：費曼對能量的見解</title><link>https://dwye.dev/post/feynman-and-energy/</link><pubDate>Tue, 24 Mar 2015 00:48:45 +0800</pubDate><guid>https://dwye.dev/post/feynman-and-energy/</guid><description>
&lt;p&gt;這是&lt;a href="https://dwye.dev/highschool-physics/"&gt;基礎物理補充文章&lt;/a&gt;的一部分。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;偉大的物理學家，也幾乎是近世普物課程之始祖：費曼 (Feynman)，在他的課程中發表了他對能量的看法：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我們發現了一個事實，有一個定律掌控了到目前為止我們所知道的自然現象，而這個定律在我們所知範圍內沒有任何的例外。而且以我們目前所知，它是準確的。這個定律被稱為「能量守恆」。它說明了有一個特定的物理量，我們稱之為「能量」，這個量在自然狀態經歷了各種變化後，並不會改變。也許聽來有些抽象，因為它是一個數學的原理：它說明了有一個量在一些事件發生時不會改變。而且並不是任何具體的物理過程描述，卻僅是一個奇特的事實：我們可以先對系統計算這些量，而當系統經歷了一些變化之後，如果我們在去計算這些量，會發現這些量加起來並沒有改變。（就好向西洋棋裡面的『象』，它只能斜著走，因此如果它原本在黑色的格字上，不管它中間動了幾步以及如何動，還是會在黑色的格子上，這就是因為這是西洋棋天生的『規則』）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;簡單歸納起來，就是&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;一個間接被定義的物理量，這個量可以在物質中轉換，並且會守恆。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;以下附上英文原文：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There is a fact, or if you wish, a law, governing all natural phenomena that are known to date. There is no known exception to this law—it is exact so far as we know. The law is called the conservation of energy. It states that there is a certain quantity, which we call energy, that does not change in the manifold changes which nature undergoes. That is a most abstract idea, because it is a mathematical principle; it says that there is a numerical quantity which does not change when something happens. It is not a description of a mechanism, or anything concrete; it is just a strange fact that we can calculate some number and when we finish watching nature go through her tricks and calculate the number again, it is the same. (Something like the bishop on a red square, and after a number of moves—details unknown—it is still on some red square. It is a law of this nature.)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="參考資料"&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.feynmanlectures.caltech.edu/I_04.html"&gt;費曼物理學講義 I-IV 能量守恆（英文）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>高中基礎物理補充：馬克士威方程式簡介</title><link>https://dwye.dev/post/maxwell-eq/</link><pubDate>Mon, 23 Mar 2015 00:48:45 +0800</pubDate><guid>https://dwye.dev/post/maxwell-eq/</guid><description>
&lt;p&gt;這是&lt;a href="https://dwye.dev/highschool-physics/"&gt;基礎物理補充文章&lt;/a&gt;的一部分。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;法拉第發現了電磁感應的現象之後，相關的應用產品就開始發展，但這時候人們主要還是注重於電磁感應現象的應用，尚未有一套理論基礎被發展出來。&lt;/p&gt;
&lt;p&gt;馬克士威正巧誕生於法拉第發現電磁感應現象那一年，這似乎註定著他之後在電磁學上的偉大貢獻：&lt;strong&gt;為電與磁的現象建立一套完整的數學理論&lt;/strong&gt;，並且用他的方程式們&lt;strong&gt;預測電磁波的存在&lt;/strong&gt;，以及&lt;strong&gt;計算光速&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;馬克士威方程式一共有四條：&lt;/p&gt;
&lt;h2 id="高斯定律"&gt;高斯定律&lt;/h2&gt;
&lt;p&gt;從一個地方產生或消失的電場大小，跟那裡的電荷密度有關。&lt;br&gt;
$$&lt;br&gt;
\nabla \cdot \mathbf{E} = \frac {\rho} {\varepsilon_0}&lt;br&gt;
$$&lt;/p&gt;
&lt;h2 id="高斯磁場定律"&gt;高斯磁場定律&lt;/h2&gt;
&lt;p&gt;從一個地方產生或消失的磁場量是零。（即&lt;strong&gt;磁場是封閉的&lt;/strong&gt;）&lt;br&gt;
$$&lt;br&gt;
\nabla \cdot \mathbf{B} = 0&lt;br&gt;
$$&lt;/p&gt;
&lt;h2 id="馬克士威-法拉第方程式"&gt;馬克士威-法拉第方程式&lt;/h2&gt;
&lt;p&gt;一個變化的磁場，會產生一個抵抗其變化的電場。&lt;br&gt;
$$&lt;br&gt;
\nabla \times \mathbf{E} = -\frac{\partial \mathbf{B}} {\partial t}&lt;br&gt;
$$&lt;/p&gt;
&lt;h2 id="馬克士威-安培方程式"&gt;馬克士威-安培方程式&lt;/h2&gt;
&lt;p&gt;一個變化的電場或是一道電流( $J$ 是電流密度)，會產生一個順應其變化的磁場。&lt;br&gt;
$$&lt;br&gt;
\nabla \times \mathbf{B} = \mu_0\left(\mathbf{J} + \varepsilon_0 \frac{\partial \mathbf{E}} {\partial t} \right)&lt;br&gt;
$$&lt;/p&gt;
&lt;h2 id="小結"&gt;小結&lt;/h2&gt;
&lt;p&gt;其中用到了一些比較進階的微積分符號，這邊就不多作深入說明，只是秀給大家看這些和現代科技息息相關的數學式。因為電磁波是由電場和磁場依據這些方程式互相感應而生成，所以理所當然地馬克士威就計算出了電磁波的速度。&lt;/p&gt;
&lt;h2 id="參考資料"&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;維基百科：&lt;a href="http://en.wikipedia.org/wiki/Maxwell%27s_equations"&gt;馬克士威方程式&lt;/a&gt;與相關頁面&lt;/li&gt;
&lt;li&gt;伊恩‧史都華 改變世界的17個方程式&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>高中基礎物理：原子內電力與重力的比較</title><link>https://dwye.dev/post/force-in-atom/</link><pubDate>Thu, 12 Feb 2015 00:48:45 +0800</pubDate><guid>https://dwye.dev/post/force-in-atom/</guid><description>
&lt;p&gt;這是&lt;a href="https://dwye.dev/highschool-physics/"&gt;基礎物理補充文章&lt;/a&gt;的一部分。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;為什麼在想像原子模型時，都是說「&lt;strong&gt;電子受到原子核的靜電力吸引&lt;/strong&gt;」而沒有考慮到「&lt;strong&gt;電子和原子核的萬有引力吸引&lt;/strong&gt;」？這邊利用&lt;a href="https://dwye.dev/s/H1Mbwesf4"&gt;估算法&lt;/a&gt;稍微估計一下兩個力的大小差別，就會發現原因，以下以氫原子為例：&lt;/p&gt;
&lt;h2 id="重力"&gt;重力&lt;/h2&gt;
&lt;p&gt;$$&lt;br&gt;
F_g = \frac{GM_1M_2}{r^2} =&lt;br&gt;
\frac{(6.67 \times 10^{-11})(9.11 \times 10^{-31})(1.67 \times 10^{-27})}{r^2} \approx&lt;br&gt;
\frac{10^{-67}}{r^2}&lt;br&gt;
$$&lt;/p&gt;
&lt;h2 id="電力"&gt;電力&lt;/h2&gt;
&lt;p&gt;$$&lt;br&gt;
F_e = \frac{kQ_1Q_2}{r^2} =&lt;br&gt;
\frac{(9 \times 10^9)(1.6 \times 10^{-19})^2}{r^2}\approx&lt;br&gt;
\frac{10^{-28}}{r^2}&lt;br&gt;
$$&lt;/p&gt;
&lt;h2 id="於是"&gt;於是&lt;/h2&gt;
&lt;p&gt;$$&lt;br&gt;
\frac{F_g}{F_e} \approx 10^{-39}&lt;br&gt;
$$&lt;br&gt;
兩個力完全不在同一個數量級上面。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原子內的電力比重力還要大上超過幾千兆倍，所以重力幾乎都被忽略掉了&lt;/strong&gt;。一般在計算原子內的電子軌域時，主要考慮的還是原子核和電子間的引力，再加上其他電子對電子的引力作修正。&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>高中基礎物理補充：終端速度簡單介紹</title><link>https://dwye.dev/post/terminal-speed/</link><pubDate>Tue, 03 Feb 2015 00:48:45 +0800</pubDate><guid>https://dwye.dev/post/terminal-speed/</guid><description>
&lt;p&gt;這是&lt;a href="https://dwye.dev/highschool-physics/"&gt;基礎物理補充文章&lt;/a&gt;的一部分。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="簡單的阻力公式"&gt;簡單的阻力公式&lt;/h2&gt;
&lt;p&gt;一般在計算自由落體時，我們忽略了空氣阻力，但實際上空氣是有阻力的。在普通物理學裡面我們可以用一個簡單的式子來估計阻力 $f$：&lt;br&gt;
$$&lt;br&gt;
f = \frac{1}{2}C\rho Av^2=kv^2&lt;br&gt;
$$&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$C$, $k$：常數&lt;/li&gt;
&lt;li&gt;$\rho$：空氣密度&lt;/li&gt;
&lt;li&gt;$A$：截面積&lt;/li&gt;
&lt;li&gt;$v$：速率&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因為下降的速率越快，劃破空氣越用力，所以空氣給的阻力就會越大，因此阻力是和下落速度有關的，即為 $f=kv^2$，或在一些學測考題裡面，會用 $f=kv$ 來估計阻力，&lt;/p&gt;
&lt;p&gt;兩者只是不同的模型而已，並沒有對錯，只有不同情況下，哪個比較準的問題。&lt;/p&gt;
&lt;h2 id="終端速度的推導"&gt;終端速度的推導&lt;/h2&gt;
&lt;p&gt;也因為阻力和速率有關，下落速率越大，可以從牛頓第二運動定律得出：&lt;br&gt;
$$&lt;br&gt;
F_合 = ma = mg - kv^2 \&lt;br&gt;
a = g - \frac{kv^2}{m}&lt;br&gt;
$$&lt;br&gt;
因此&lt;strong&gt;加速度會隨著速率的上升而變小&lt;/strong&gt;，可大略畫出 $v$-$t$ 圖如下（斜率即加速度）：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://i.imgur.com/fWk3fJ1.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;當加速度減小到零的時候，速度變成一直線，這時候就稱為「&lt;strong&gt;終端速度&lt;/strong&gt;」，是物體在下落過程中所能達到的&lt;strong&gt;最大速度&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;附帶一提，物體是永遠無法達到終端速度的，只會不斷接近它。終端速度所形成的直線是一條「漸進線」，物體的速度曲線會逐漸靠近它，但永不相交。&lt;/p&gt;
&lt;h2 id="參考資料"&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Halliday &amp;amp; Resnick, Fundamentals of Physics&lt;/li&gt;
&lt;li&gt;102學測自然科試題&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="圖片來源"&gt;圖片來源&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;102學測自然科試題&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>高中基礎物理補充：白話微積分</title><link>https://dwye.dev/post/differentiation-and-integration/</link><pubDate>Wed, 21 Jan 2015 00:48:45 +0800</pubDate><guid>https://dwye.dev/post/differentiation-and-integration/</guid><description>
&lt;p&gt;這是&lt;a href="https://dwye.dev/highschool-physics/"&gt;基礎物理補充文章&lt;/a&gt;的一部分。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="微分"&gt;微分&lt;/h2&gt;
&lt;p&gt;微分，可以求出一個函數的&lt;strong&gt;斜率函數&lt;/strong&gt;，只要把斜率函數內的任何一個x座標帶入，就能得到原本函數在x座標的斜率。&lt;/p&gt;
&lt;p&gt;以下圖來說，求出一點的斜率似乎沒有很直觀的數學算式，&lt;strong&gt;要有斜率就必須要有一條線，要定出一條線則必須在平面上找到兩個點&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://i.imgur.com/KxdBYBR.png" alt="differentiation from Salas, Calculous one and several variables 10/e"&gt;&lt;/p&gt;
&lt;p&gt;所以這邊用的方式就是，除了我們要的點之外，先另外取一個點 $x+h$，畫出割線。這時候發揮想像力，把另取的點 $x+h$ 沿著函數，不斷往 $x$ 的位置移動，&lt;strong&gt;越靠近 $x$ 越好&lt;/strong&gt;，但是不能碰到 $x$ ，不然重合成一個點的話就無法決定一條線了。&lt;/p&gt;
&lt;p&gt;這樣我們就能得到一個很接近切線的割線，同時這個割線的斜率也會很接近函數在該點的斜率。現在以函數 $f(x)=x^2$ 當作例子，用算式求出斜率的過程表示如下：&lt;br&gt;
$$&lt;br&gt;
x點的斜率 = \frac{f(x+h)-f(x)}{(x+h)-x} =&lt;br&gt;
\frac{(x+h)^2 - x^2}{h} =&lt;br&gt;
\frac{2xh - h^2}{h} = 2x+h&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;因為我們不斷把兩個點靠近，$h$ 就會縮小到可以忽略的地步，因此上述的答案就變成 $2x$，這正是 $f(x)=x^2$ 的斜率函數，或著用更正式的名字，叫「&lt;strong&gt;導函數&lt;/strong&gt;」。&lt;/p&gt;
&lt;p&gt;而這個過程，我們會說是「&lt;strong&gt;把 $f(x)$ 對 $x$ 微分」&lt;/strong&gt;。這套工具是牛頓為了解決運動學上的問題所發明的，只要把 $x$-$t$ 圖中的 $x$ 對 $t$ 微分，就可以求出任何時刻&lt;strong&gt;位置對時間的變化，也就是速度&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;牛頓除了這項工具之外，他還有另外一項利器：積分。&lt;/p&gt;
&lt;h2 id="積分"&gt;積分&lt;/h2&gt;
&lt;p&gt;積分，可以求出一個函數底下和橫軸所夾的&lt;strong&gt;面積&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;這個面積所形成的函數 $F(x)$，就剛好是&lt;strong&gt;微分的反過來操作&lt;/strong&gt;。只要把 $F(x)$ 微分之後，就可以得到原本的函數 $f(x)$。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://i.imgur.com/otu3UM2.png" alt="integration from Salas, Calculous one and several variables 10/e"&gt;&lt;/p&gt;
&lt;p&gt;如圖，積分的想法就是把一個不規則的函數切成很多很多小塊的長方形。原本的函數面積很複雜，沒有公式可以算，但切成小塊的長方形之後，我們就可以分別計算長方形的面積公式，加起來，就可以得到整塊函數底下的面積。圖中的長方形面積也許不夠準，但是只要切越多塊，所有長方形的面積就會越接近函數和橫軸所夾的真正面積。&lt;/p&gt;
&lt;p&gt;通常在計算積分的時候，只要把微分的法則反過來運用就成了。不過也因為沒有較直接的運算方法，所以積分的運算通常比微分複雜，甚至會有無法直接積分的情況發生。&lt;/p&gt;
&lt;h2 id="其他關於微積分"&gt;其他關於微積分：&lt;/h2&gt;
&lt;p&gt;除了牛頓之外，另一個科學家：&lt;strong&gt;萊布尼茲，也獨立發展出了這套工具&lt;/strong&gt;，他和牛頓常常被視為微積分的發明者。不過這套工具一直受到質疑，因為它的「無限接近」的概念一直沒有數學上嚴謹的定義，這個問題一直到19世紀時才被一些數學家所解決，並成為現在大學生最入門的科目：「微積分Calculous」。（英文Calculous是計算的意思，不過因為教學內容主要還是要提供微積分這套工具，因此中文名稱才稱呼叫微積分。）&lt;/p&gt;
&lt;h2 id="參考資料"&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;伊恩‧史都華 改變世界的17個方程式&lt;/li&gt;
&lt;li&gt;容志輝老師 101-1微積分甲上 課程筆記&lt;/li&gt;
&lt;li&gt;Salas, Calculous one and several variables 10/e&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="圖片來源"&gt;圖片來源&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Salas, Calculous one and several variables 10/e&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>高中基礎物理補充：基本粒子大觀園</title><link>https://dwye.dev/post/elementary-particles/</link><pubDate>Fri, 12 Dec 2014 00:48:45 +0800</pubDate><guid>https://dwye.dev/post/elementary-particles/</guid><description>
&lt;p&gt;這是&lt;a href="https://dwye.dev/highschool-physics/"&gt;基礎物理補充文章&lt;/a&gt;的一部分。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="基本粒子"&gt;基本粒子&lt;/h2&gt;
&lt;p&gt;基本粒子，是科學家對於物質所認知的最小單位，隨著科學的進展，最小單位不斷的被更新。現今物質的最小單位，最廣為所知的大概就是電子和夸克。不少的基本粒子因為生存週期不長，或和一般人認知的物質概念有所相左，所以比較不被熟悉。&lt;/p&gt;
&lt;h2 id="費米子"&gt;費米子&lt;/h2&gt;
&lt;p&gt;最為人所知的夸克大概就是「上夸克」（帶電+2/3基本電荷）和「下夸克」（帶電-1/3基本電荷），這兩種夸克三個一組靠強力結合在一起，形成質子（2上1下）和中子（1上2下），這兩個夸克是所謂「第一代」被發現的粒子。後來還有科學家發現其他的夸克，目前已知六種，且它們的共同特性就是：不會單獨存在，夸克通常是成對或是三個一組形成較重的粒子。&lt;/p&gt;
&lt;p&gt;電子屬於相對於夸克的「輕子」，不參與強交互作用的粒子。電子也屬於「第一代」，同時是最早被認知的基本粒子。&lt;strong&gt;夸克和電子屬於「費米子」，意指自旋量（參見高三化學）是半整數（以電子為例：±1/2）的粒子&lt;/strong&gt;。它們的共同特性就是：遵守&lt;strong&gt;鮑立不相容定則&lt;/strong&gt;，有個簡單的比喻，就是兩個費米子不能放在同一個位置。&lt;/p&gt;
&lt;h2 id="玻色子"&gt;玻色子&lt;/h2&gt;
&lt;p&gt;除了費米子之外，當然也有&lt;strong&gt;自旋量是整數（包含0）的粒子，這些粒子稱為玻色子&lt;/strong&gt;，它們不遵守鮑立不相容定則，可以放在完全相同的位置而發生「玻色－愛因斯坦凝聚」。目前已知的玻色子分為兩種：「規範玻色子」、「希格斯玻色子」。&lt;/p&gt;
&lt;p&gt;自然界的交互作用（也就是力，參見基礎物理第四章），必須透過規範玻色子來進行，因此又可以依交互作用將規範波色子繼續分類。和重力作用有關的是「引力子」，但由於重力太弱，導致引力子一直都沒有被發現；和強力有關的是「膠子」，維持原子核和質子、中子的穩定存在；而和電磁力作用有關的，正是我們&lt;strong&gt;常見的電磁波的本尊：「光子」&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;希格斯玻色子，是最近才被實驗上偵測到的基本粒子，詳細性質很多都還是謎團，但科學家預測這個粒子可能是質量的來源。&lt;/p&gt;
&lt;h2 id="反粒子"&gt;反粒子&lt;/h2&gt;
&lt;p&gt;很多粒子有相對應的反粒子存在，例如：電子有相對應的「正子」存在。但也有反粒子就是其本身的例子，例如：光子。粒子構成物質，反粒子構成反物質。&lt;strong&gt;反物質和物質接觸時，會立刻遵守愛因斯坦的質能轉換公式而化為能量&lt;/strong&gt;。原本宇宙產生時，一般物質和反物質的數量應該要一樣，但可能是因為衰變速率不同，導致最後剩下物質存在我們熟悉的世界，反物質則灰飛煙滅。不過科學家現在已經有技術在實驗室裡面短暫地呼喚出反物質。&lt;/p&gt;
&lt;h2 id="參考資料"&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://zh.wikipedia.org/wiki/%E5%9F%BA%E6%9C%AC%E7%B2%92%E5%AD%90"&gt;維基百科：基本粒子和相關頁面&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;科技部高瞻自然科學教學資源平台：&lt;a href="https://highscope.ch.ntu.edu.tw/wordpress/?p=19087"&gt;基本粒子&lt;/a&gt;、&lt;a href="https://highscope.ch.ntu.edu.tw/wordpress/?p=41214"&gt;反粒子&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Carter, Classical and Statistical Thermodynamics：費米子、玻色子的統計性質。&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>高中基礎物理補充：寫在三態之外</title><link>https://dwye.dev/post/state-phase-plasma/</link><pubDate>Sun, 07 Dec 2014 00:48:45 +0800</pubDate><guid>https://dwye.dev/post/state-phase-plasma/</guid><description>
&lt;p&gt;這是&lt;a href="https://dwye.dev/highschool-physics/"&gt;基礎物理補充文章&lt;/a&gt;的一部分。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;物質有三態：固態、液態、氣態。乍聽起來就像物質有三寶一樣，但是其實物質並不是只有三寶！&lt;/p&gt;
&lt;h2 id="狀態state"&gt;狀態(State)&lt;/h2&gt;
&lt;p&gt;「狀態」的意思，是說分子排列的緊密程度。固態，是緊密排列；液態，分子很靠近但可以滑動；氣態，分子之間是自由的，所以才能有不同體積。&lt;/p&gt;
&lt;p&gt;除了這三種狀態之外，另外還有「&lt;strong&gt;電漿態(plasma)&lt;/strong&gt;」，把氣體繼續加溫，有機會使分子游離，形成電子和離子以及中性分子的混合狀態。現在更有技術可以在不那麼高溫的情況下做出電漿。&lt;/p&gt;
&lt;p&gt;電漿態物質並不稀有，例如：電漿電視、大氣電離層、一些火焰尖端、太陽之類的恆星。電漿態和一般所知的三態，常被統稱為物質基本四態。&lt;/p&gt;
&lt;h2 id="相phase"&gt;相(Phase)&lt;/h2&gt;
&lt;p&gt;只要&lt;strong&gt;分子的排列方式不一樣，就算是不同的「相」&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;最簡單的例子，就是&lt;strong&gt;鑽石和石墨&lt;/strong&gt;，雖然都是碳原子組成，但沒有人會認為它們是一樣的東西，不管是顏色、硬度、導電性，甚至是價值，都天差地遠，所以把它們歸為不同的「相」也是合情合理。&lt;/p&gt;
&lt;p&gt;以水為例，大家所熟悉的固態就是日常生活之間所見的冰，但是其實水的固態不只一種相，目前已經發現水在不同溫壓情況下，會有很多不同的排列方式。詳細可以參考&lt;a href="https://highscope.ch.ntu.edu.tw/wordpress/?p=53503#more-53503"&gt;這篇&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id="參考資料"&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;維基百科：&lt;a href="http://zh.wikipedia.org/wiki/%E7%89%A9%E8%B4%A8%E7%8A%B6%E6%80%81"&gt;物質狀態&lt;/a&gt;、&lt;a href="http://zh.wikipedia.org/wiki/%E7%AD%89%E7%A6%BB%E5%AD%90%E4%BD%93"&gt;電漿&lt;/a&gt;、&lt;a href="http://zh.wikipedia.org/wiki/%E7%9B%B8_%28%E7%89%A9%E8%B4%A8%29"&gt;相&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://highscope.ch.ntu.edu.tw/wordpress/?p=53503#more-53503"&gt;科技部高瞻自然科學教學資源平台：水的三相點不只一個&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Silbey, Physical Chemistry 4/e&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>高中基礎物理補充：巨觀與微觀</title><link>https://dwye.dev/post/macrocosm-and-microcosm/</link><pubDate>Tue, 02 Dec 2014 00:48:45 +0800</pubDate><guid>https://dwye.dev/post/macrocosm-and-microcosm/</guid><description>
&lt;p&gt;這是&lt;a href="https://dwye.dev/highschool-physics/"&gt;基礎物理補充文章&lt;/a&gt;的一部分。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="巨觀"&gt;巨觀&lt;/h2&gt;
&lt;p&gt;巨觀，簡單說起來，就是我們日常生活所看到的。&lt;/p&gt;
&lt;p&gt;像是桌子、汽車、電燈……，這些東西你不會看得到他們是由一粒粒分子組成的，它們看起來就是彼此不同的東西，桌子就是桌子，你不會發現桌子跟玻璃在分子排列上面有什麼差別，你最多只會發現桌子跟椅子都是木頭做的。&lt;/p&gt;
&lt;p&gt;如果我們使用一些光學儀器輔助，讓我們可以看到更小的細菌、木頭上的紋路，&lt;strong&gt;很抱歉，這些都還是巨觀，因為你還是看不到這些東西是由一粒粒分子構成的&lt;/strong&gt;，你也沒辦法看見空氣中有氧分子和氮分子悠閒自在地作飛來飛去。&lt;/p&gt;
&lt;h2 id="微觀"&gt;微觀&lt;/h2&gt;
&lt;p&gt;微觀，簡單說起來，就是從&lt;strong&gt;原子或分子&lt;/strong&gt;層面看。&lt;/p&gt;
&lt;p&gt;主要是在十九世紀至今才開始慢慢興盛，因為古典電磁學和其他各種學問的發展，科學家才能想辦法用實驗和計算的方式，慢慢探索我們看不到的世界。微觀下的世界，有時候跟牛頓直接看到的巨觀世界會不太符合，因此才有所謂量子理論的發展。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;想像我們拿著刀子切蛋糕，蛋糕似乎可以切多小就切多小，但是總有一天我們會把蛋糕切到只剩一顆粒子，不能再分割。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;量子的概念就是把這種想法推廣。&lt;/p&gt;
&lt;h2 id="巨觀是微觀的統計結果"&gt;巨觀是微觀的統計結果&lt;/h2&gt;
&lt;p&gt;雖然微觀世界可能和巨觀世界有些差異，例如說，巨觀世界主要使用牛頓力學，但在微觀下必須使用量子力學。但是如果我們&lt;strong&gt;在微觀世界看到的很多事情用統計的方式統合起來，那就會得到巨觀下看到的結果。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;物理學上的例子，就是統計物理可以利用微觀的想法，由下而上（Bottom-Up）建構出和熱力學從巨觀觀察，由上而下（Top-Down）建立的理論有相同的結果。&lt;/p&gt;
&lt;h2 id="參考資料"&gt;參考資料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;I. N. Levine, Quantum Chemistry&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>公斤、公斤重與牛頓的差別</title><link>https://dwye.dev/post/kg-kgw-n/</link><pubDate>Mon, 17 Nov 2014 00:50:40 +0800</pubDate><guid>https://dwye.dev/post/kg-kgw-n/</guid><description>
&lt;p&gt;這是&lt;a href="https://dwye.dev/highschool-physics/"&gt;基礎物理補充文章&lt;/a&gt;的一部分。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;很多人都有類似方面的問題，就一次寫清楚&lt;/p&gt;
&lt;h2 id="日常生活"&gt;日常生活&lt;/h2&gt;
&lt;p&gt;在日常生活裡，重量和質量是差不多的（都在地表）&lt;br&gt;
公斤這個單位因此可以在兩者之間混用&lt;br&gt;
「小明的體重（重量）是 60 公斤 (kg)」&lt;br&gt;
這句話是沒有問題的&lt;/p&gt;
&lt;h2 id="物理"&gt;物理&lt;/h2&gt;
&lt;p&gt;物理裡面，重量跟質量是不同的東西&lt;br&gt;
因此必須先幫它們下個比較精確的定義（至少於高中範圍可以通用）&lt;/p&gt;
&lt;h3 id="質量--m-"&gt;質量 $ (m) $&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;物體本身具有的本性，單位為公斤 $ (kg) $&lt;/li&gt;
&lt;li&gt;和當地重力加速度無關&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="重量--w-"&gt;重量 $ (W) $&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;物體所受的萬有引力，單位為公斤重 $ (kgw) $、牛頓 $ (N) $&lt;/li&gt;
&lt;li&gt;和當地重力加速度有關&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;重量 = 重力 = 物體所受之引力&lt;br&gt;
所以可以說重量是一種力&lt;br&gt;
重量與質量的關係為&lt;br&gt;
$$ W = m \times g $$&lt;br&gt;
可以和牛頓第二運動定律做比較&lt;br&gt;
$$ F = m \times a $$&lt;br&gt;
重力加速度 $(g)$ 對應到加速度 $(a)$&lt;br&gt;
重量 $(W)$ 對應到力 $(F)$&lt;br&gt;
所以重量的國際標準單位和力一樣，是牛頓$(N)$&lt;br&gt;
但方便起見，我們另外定義在地表質量 $1kg$ 的物體的重量為 $1kgw$&lt;/p&gt;
&lt;h3 id="當-地表重力加速度--g--98-ms2-"&gt;當 地表重力加速度 $ g =\ 9.8\ m/s^2 $&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;質量 $ 1\ kg $ 的物體 重量 $ 1\ kgw $ 或 $ W = mg = 1 \times 9.8 = 9.8 N $&lt;/li&gt;
&lt;li&gt;所以 $ 1\ kgw = 9.8\ N $&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="當-地表重力加速度近似為-g--10-ms2-"&gt;當 地表重力加速度近似為 $g =\ 10\ m/s^2 $&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;質量 $ 1\ kg $ 的物體 重量 $ 1\ kgw $ 或 $ W = mg = 1 \times 10 = 10 \ N$&lt;/li&gt;
&lt;li&gt;所以 $ 1\ kgw = 10\ N $&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="註"&gt;註&lt;/h3&gt;
&lt;p&gt;就算不是地表的地方&lt;br&gt;
公斤重和牛頓的單位轉換，仍必須以地表重力加速度去看&lt;br&gt;
因為定義是在地表上的重量&lt;/p&gt;
&lt;h2 id="所以我們必須說"&gt;所以我們必須說&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;「小明的質量是 60 公斤」（在任何地方都成立）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="在地表重力加速度-g--10-ms2--時"&gt;在地表重力加速度 $g = 10\ m/s^2 $ 時&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;「小明在地表的重量是 60 公斤重」&lt;/li&gt;
&lt;li&gt;「小明在地表的重量是 600 牛頓」&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="在某個-g--5-ms2-地球表面的一半-的行星"&gt;在某個 $g = 5\ m/s^2 $(地球表面的一半) 的行星&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;$ W = mg = 60 \times 5 = 300 $ ( $ F = ma $ )&lt;br&gt;
「小明在該行星的重量是 300 牛頓」&lt;/li&gt;
&lt;li&gt;$ 300\ N = 30\ kgw $ (單位轉換)&lt;br&gt;
「小明在該行星的重量是 30 公斤重」&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="或是在太空中沒有重力加速度--g--0--"&gt;或是在太空中，沒有重力加速度( $ g = 0 $ )&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;$ W = mg = 60 \times 0 = 0 $&lt;br&gt;
「小明在太空中的重量是 0 」&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;以上，希望能解決大家的困擾&lt;br&gt;
有問題歡迎提出&lt;br&gt;
也歡迎參照 PTT &lt;a href="//www.ptt.cc/bbs/SENIORHIGH/M.1416156646.A.713.html"&gt;原文&lt;/a&gt;&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>在補習班的日子</title><link>https://dwye.dev/post/20140828_164254/</link><pubDate>Thu, 28 Aug 2014 16:42:54 +0800</pubDate><guid>https://dwye.dev/post/20140828_164254/</guid><description>
&lt;p&gt;這是篇公開心得，所以避開了所有人名以及公司名，有必要者用綽號稱之。&lt;/p&gt;
&lt;h2 id="緣起"&gt;緣起&lt;/h2&gt;
&lt;p&gt;2012年11月，第一次到13樓的辦公室考試，那邊的人說，考卷就在桌上，寫完考卷和資料之後就可以走了，之後靜待通知。&lt;/p&gt;
&lt;p&gt;所有事情的發生，都有它的背景以及緣起。大學時代決定邁向經濟獨立目標的我，四處尋找工作機會。因緣際會下有了第一個家教，但是總覺得還不夠，於是就問了原本已在補習班工作的高中學長D大，決定先到補習班累積經驗。&lt;/p&gt;
&lt;h2 id="始"&gt;始&lt;/h2&gt;
&lt;p&gt;走出補習班，我立刻衝到斜對角街上的墊腳石書店，走上二樓，找到93年指考試題的詳解。因為有事先練習歷屆試題，憑印象對完答案發現只錯了一題，應該是能成功錄取了！&lt;/p&gt;
&lt;p&gt;第一次上班是學長帶我，那時候身為菜鳥，什麼都不知道，連解個題都會被學長看不下去喊卡。一開始只是到補習班寫詳解，有空學長會讓我試著解一些題目給學生看，慢慢的從同事的身上學習，從學生的反應再調整，約兩個禮拜之後，我也開始有自己的時段，並且放上線上預約。&lt;/p&gt;
&lt;p&gt;這家補習班的制度甚完備，至少解題輔導方面做得很好，學生可以預先在網路上預約我們，只要在指定的時間來補習班一趟就能有專人為自己解惑，省去現場排隊的困擾，解題時間也夠長。若是不幸每個老師被都預約滿了，也能到現場直接抽號碼排隊，雖然就失去了預約的好處，但補習班也有提供足夠的等待區座位，有桌椅以及沙發，算是也有個地方讓自己念書而不至於讓時間白白流失。&lt;/p&gt;
&lt;p&gt;D大和我們的主管似乎還蠻要好的，我也和他聊過幾次天。我覺得這裡和我以前高中補習的地方比起來，似乎少了一點商業氣息，而多了一些真正為學生著想的感覺。這裡的學生大多都很喜歡這裡；所謂的名師願意在下課後為學生多留一個小時以上幫學生解答上課疑惑；我們兼職的也是抱著開心的心情來上班，彼此之間感情好；補習班也不會像少數同業（我高中補的那家很誇張）四處公開攻擊他人，我覺得，真是來到一個對的地方。&lt;/p&gt;
&lt;h2 id="適應"&gt;適應&lt;/h2&gt;
&lt;p&gt;每到學測或指考將近，補習班因為有開衝刺班級，所以解題的需求就會大增，為了讓學生的疑問都能夠及時得到解答，所以我們也必須在學期末加班。大多數的解題老師都是大學生，所以我們就勢必得學習在工作與課業的時間分配上達到均衡，避免讓原本就很爆炸的期末更加爆走。&lt;/p&gt;
&lt;p&gt;第一年，是平淡的度過，也許是我的解題實力還沒有很夠，或是學生沒有特別注意到我，上班的時候常常閒閒的，每次都帶書去讀還能領薪水也是不錯，不像其他熱門的同事基本上都是從上班解到下班。不過也是有認識一些學生，還有在隨班的時候念普物念到一半有學生主動XU我。有時候有人請假，就必須找人「代班」，隨著上過的班越多，有時候除了自己的時段也能認識一些來代班的人，或是藉由代班和其他時段的人有所互動。也有一次在幫其他人代班的時候，有學生問我是在哪一天的班，我說是禮拜五晚上，但是那個學生禮拜五晚上要補別的科目，真是太為他感到可惜了。不過會被這樣問，應該也是覺得我解的不錯吧。&lt;/p&gt;
&lt;p&gt;在我進來補習班的一個禮拜後，馬上就有另外一位新人進來，但是就年紀來說他可是大我整整四個年級，不過看起來很像和我同年的，直到我問到他讀哪裡時才嚇到 XD。後來我們常常一起接同一個時段，所以變得比較熟，常常互相交流神魔，這是第二年的事情了。&lt;/p&gt;
&lt;h2 id="上手"&gt;上手&lt;/h2&gt;
&lt;p&gt;第二年我接了最熱門的禮拜六下午時段，不知道是時段加持還是實力提升，開始了週週爆滿的上班生活。每個禮拜都是中午12點打卡，先幫早上的班解題，然後買午餐吃午餐，下午一點開始解預約，一路解到五點半，甚至常常加班到六點。工作最晚的一次，是加班打到七點半的卡，那次是段考前吧，剛好我沒事，想說學生都辛苦來一趟了沒有解決完他問題實在對不起他們，於是就加了兩個小時的班。&lt;/p&gt;
&lt;p&gt;因為預約制度的關係，有時候系統一開放，學生就會立刻登入搶走自己最常預約的老師。這種對自己很死忠的學生，我們常常私下稱其為「固定粉絲」，簡單說起來就是每周上班都會看到他。通常固定粉絲都和自己很好，所以解起題來也是氣氛輕鬆，偶爾也能聊個天關心一下學生的近況，算是忙碌中的一點小休閒。對了，補習班的規定白紙黑字寫說不能讓學生知道自己的電話，可能是怕我們和學生私下聯絡甚至變成家教搶走補習班的生意吧，或是發生所謂禁忌的師生戀情（我還真的看過&amp;hellip;&amp;hellip;），不過，現在早就已經不是電話時代了，所以&amp;hellip;&amp;hellip;.。&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;h2 id="轉折"&gt;轉折&lt;/h2&gt;
&lt;p&gt;在補習班解題並不像家教，我們其實不必對學生的成績負責，也是補習班時薪低上很多的合理原因之一吧？只是我們終究不是解題機器，和固定粉絲熟了之後，也是會關心他們的成績，會問他們在班上的排名，會好奇他們學測考得如荷，甚至是上了大學何去何從。也因此有時候當時常會找自己的學生考得沒有很理想的時候，其實也會偷偷跟著難過，就像是家教學生一樣。這反而是補習班矛盾的地方吧？當然也有碰過學生在畢業之後約老師出來吃飯聊天甚至出來玩的情況，反正畢業之後大家都是大學生了，也不受到補習班的規範了。&lt;/p&gt;
&lt;p&gt;第二年也有新的學弟妹進來解題，變成我來負責帶菜鳥了，換個角度看他們，好像當初進來時學長看自己的感覺一樣，頓時覺得自己老了好幾歲，不過也因為自己的成長而感到興奮。在這裡待了第二年之後，認識的人變得很多，常常很自在的上班，偶爾空閒時也能和其他人聊天互話家常。有時候會私下揪出來吃飯，也有因為是同學校的可以交流選課與社團經驗等等，或是聊到學術上的話題。有時候也會聊到一點上頭認為比較禁忌的話題：補習班的八卦。&lt;/p&gt;
&lt;p&gt;在這裡待久了，會漸漸感受到其實一切並沒有那麼單純，有的時候還是會有一些暗鬥的傳聞出來，也有一些老師的負面八卦，不過或許是杞人憂天，又或是有人故意散播謠言，誰知道呢？知道有人對主管新策的不滿，有人因為太特異的行為屢勸不聽被解雇了，或是有同事之間出現了感情糾紛，也會對自己工作的地方多了一點眼界，但整體而言這裡還是我認為不錯的工作環境，對學生來說也是不錯的補習班。總言之，有八卦的地方，就是交流互動多的地方，也是我認為的好地方。&lt;/p&gt;
&lt;p&gt;光明的背後一定有陰影，事情有正面一定有反面。因為我工作的這家補習班不算小，所以有時候必須到其他的分部排班，其中板橋離我們每個人的生活圈都很遠，一開始補習班還有給我們車馬費，但後來就取消了。每學期我們都要推派倒楣鬼過去，搭捷運過去要40分鐘，真的不是很方便，還好我從頭到尾都沒有排過那裏的班。&lt;/p&gt;
&lt;p&gt;最讓我挫折的一件事情，也是唯一我在這邊挫折的一件事，就是薪水。其實打從第二年下半年以來，就一直有著是否要辭退的想法。一是我實力夠了，實在不需要繼續在這累積經歷；二就是這段要提到的薪水。前面有提到補習班有招收衝刺班，所以期末總是需要我們加班幫忙。但是我因為有社團有家教，本身就是比較忙的，實在難以空出很多時間，有時候期末考週多接個三、四段班就是極限了。總是有明言暗語中提點著我們，衝刺班能夠排的段數越多，越能代表你對補習班的忠心程度，因此也是薪水調升的一項主要依據中。我認為，我的實力不比其他人差，年資也比新進的學弟妹多上一年，但是就因此我的薪水永遠都是起薪，相較同期進來的其他人來說實在不太服氣，老實說對於有經濟壓力的我而言，這也是最後選擇離開的理由之一。&lt;/p&gt;
&lt;p&gt;更何況，我的一位高中好友，在別家補習班已經做到上台的境界了，當然這是制度的差異，然而我還年輕，還有往上爬的野心，實在不想如此禁錮自己。只是諒在自己在補習班已經有一定人脈，每次上班也蠻開心的，更何況每週都有學生等著要問我問題呢，實在不忍心拋下任何人，就先把這件事情擱著。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;我是為了學生而努力，而不是為補習班而努力&lt;/strong&gt;。著這樣的心情，我又多做了一段時間。&lt;/p&gt;
&lt;p&gt;有時候，學生也會在畢業之後私下給我們一點回饋，像是我曾經收過學生送的蛋糕與食物等等，也有人收過學生的卡片，還有與學生合照等等。其實有時候覺得他們能夠得到幫助而滿意地感謝我們，是身為解題老師最好的回報。&lt;/p&gt;
&lt;h2 id="曲終"&gt;曲終&lt;/h2&gt;
&lt;p&gt;宴會過後，人終將散去。進來補習班打工，總有離開的一天。趁著暑假，常問我的學生都考上了大學之後，利用這個時機離開，是對一切都傷害最小的。當經驗累積到一定程度，當實力培養到一個境界，我在這邊能學的我敢說至少學到了八成，剩下的我到外面也能學更多。就像我高中補數學一樣，只是補個滋味，知道了老師教法和讀書方法，我就不繼續報名了。選擇是在一個晚上之間決定的，至少這樣自行完美的離去，總比哪天不小心犯錯被辭退，或是哪天和誰發生爭執而自行引退還要好。&lt;/p&gt;
&lt;p&gt;這兩年，謝謝一切：謝謝帶領過我的兩位主管；謝謝數位同是解物理的同事們一起分擔許多的學生以及麻煩；謝謝老師帶領整個補習班的物理團隊；謝謝和我一起上過班的其他同事，能夠和我聊天交流，認識你們真好；謝謝所有問我的學生，能夠捧我的場，祝你們有美好的大學生活，還沒考試的能考出不錯的成績；最後的最後，謝謝D大，讓我有機會接觸這裡。這裡很好，是我自己選擇了離開，也祝福所有還在工作的人，繼續加油，讓學生能夠考上自己理想的志願。&lt;/p&gt;
&lt;p&gt;現在是2014年8月。&lt;/p&gt;
&lt;p&gt;有時候講到情緒激動處，雖沒有臨表涕泣，但仍會不知所云，請多包涵 XD&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>估算法</title><link>https://dwye.dev/post/%E4%BC%B0%E7%AE%97%E6%B3%95/</link><pubDate>Wed, 20 Aug 2014 17:28:39 +0800</pubDate><guid>https://dwye.dev/post/%E4%BC%B0%E7%AE%97%E6%B3%95/</guid><description>
&lt;p&gt;雖然大多數考題數字皆有設計過，但若選項間數字差異很大，懂得一些估算技巧，可以增加答題速度。以下面這題為例：(92學測補考)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;南、北極的冰帽如果真的全部融化，粗略預估全球海平面最可能會上升多少高度？&lt;br&gt;
（提示：全球海洋的平均深度約為 $4000\ \mathrm{m}$；南極大陸的冰層厚度約 $2.2\ \mathrm{km}$。面積約為 $13,000,000\ \mathrm{km}^2$；格林蘭的面積約 $1,700,000\ \mathrm{km}^2$，冰層厚度約 $1.5\ \mathrm{km}$；地球半徑約 $6400\ \mathrm{km}$）&lt;br&gt;
(A) 60&lt;del&gt;80 毫米 (B) 60&lt;/del&gt;80 公分 (C) 60&lt;del&gt;80 公尺 (D) 600&lt;/del&gt;800 公尺 (E) 6~8 公里&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="列出算式"&gt;列出算式：&lt;/h2&gt;
&lt;p&gt;善用科學記號，有助於我們接下來要進行的簡化！（算式內單位皆統一為公斤）&lt;br&gt;
$$&lt;br&gt;
上升高度 = \frac{體積增加}{海洋面積} = \frac{2.2 \times 1.3 \times 10^7 + 1.5 \times 1.7 \times 10^6}{4 \pi \times (6.4 \times 10^3)^2 \times 0.7}&lt;br&gt;
$$&lt;/p&gt;
&lt;h2 id="太難算了吧"&gt;太難算了吧！&lt;/h2&gt;
&lt;p&gt;這麼一長串的算式，基本上科技進步的現代，都是丟給計算機之神casio 991ES解決。然而，考場如戰場，敲計算機其實也是需要時間的，更何況這個戰場（學測）還不給帶計算機。因此，正是估算法大顯神威的時候了。&lt;/p&gt;
&lt;h2 id="估算法登場"&gt;估算法登場！&lt;/h2&gt;
&lt;p&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;h3 id="先算次方"&gt;先算次方&lt;/h3&gt;
&lt;p&gt;先執行第一原則，我們把科學記號的 10 的次方全部拆出來，上述算式可以拆成：&lt;br&gt;
$$&lt;br&gt;
\frac{2.2 \times 1.3 + 1.5 \times 1.7 \times 0.1}{4 \pi \times 6.4^2 \times 0.7} \times&lt;br&gt;
\frac{10^7}{10^{3 \times 2}} =&lt;br&gt;
\frac{2.2 \times 1.3 + 1.5 \times 1.7 \times 0.1}{4 \pi \times 6.4^2 \times 0.7} \times 10&lt;br&gt;
$$&lt;br&gt;
其實大部分的題目，做到這裡，答案就呼之欲出了。&lt;/p&gt;
&lt;p&gt;不過這題要注意，各選項位數並沒有差上很多，且前項分子可能比分母大一個數量級，所以分數部分也要進一步估算，通常是需要一點經驗的：&lt;/p&gt;
&lt;h3 id="進一步估算"&gt;進一步估算&lt;/h3&gt;
&lt;p&gt;估算的時候，別忘了使用$\approx$（或是台灣國高中以下常用$\fallingdotseq$）代表大約等於喔。&lt;/p&gt;
&lt;p&gt;按照第二原則，先處理加減，第一步將明顯小了十倍的 $1.5 \times 1.7 \times 0.1$ 直接拔掉：&lt;/p&gt;
&lt;p&gt;$$&lt;br&gt;
\frac{2.2 \times 1.3 + 1.5 \times 1.7 \times 0.1}{4 \pi \times 6.4^2 \times 0.7} \approx&lt;br&gt;
\frac{2.2 \times 1.3}{4 \pi \times 6.4^2 \times 0.7}&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;接著利用第三原則將 $2.2 \times 1.3$ 變成 $2.0 \times 1.5$：&lt;/p&gt;
&lt;p&gt;$$&lt;br&gt;
\frac{2.2 \times 1.3}{4 \pi \times 6.4^2 \times 0.7}\approx&lt;br&gt;
\frac{2.0 \times 1.5}{4 \pi \times 6.4^2 \times 0.7}&lt;br&gt;
= \frac{3}{4 \pi \times 6.4^2 \times 0.7}&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;最後將 3 和 $\pi\ (\approx 3.14)$ 約掉。&lt;/p&gt;
&lt;p&gt;$$&lt;br&gt;
\frac{3}{4 \pi \times 6.4^2 \times 0.7} \approx \frac{1}{4 \times 6.4^2 \times 0.7}&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;接下來的計算就簡單了，可以直接硬算。但這篇我們就堅持估算法到底，當作估算法的練習吧！&lt;/p&gt;
&lt;p&gt;為了方便起見，這裡可能要做暴力一點的估算，直接拿整數近似，一樣用到第三原則，把 $6.4^2 \times 0.7$ 變成 $6^2 \times 1$：&lt;br&gt;
$$&lt;br&gt;
\frac{1}{4 \times 6.4^2 \times 0.7} \approx&lt;br&gt;
\frac{1}{4 \times 6^2 \times 1} =&lt;br&gt;
\frac{1}{4 \times 36} =&lt;br&gt;
\frac{1}{144} =&lt;br&gt;
\frac{1000}{144} \times 10^{-3}&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;最後回到第一原則，把次方拿出來。&lt;/p&gt;
&lt;h3 id="整理結果"&gt;整理結果&lt;/h3&gt;
&lt;p&gt;綜合第一步拿到的 10 的一次方，可以知道答案約是：&lt;br&gt;
$$&lt;br&gt;
\frac{1000}{144} \times 10^{-2}\ \mathrm{km}&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;前面那項大約為 7（其實選項也告訴我們，是在 6 到 8 之間），所以答案會落在 $7 \times 10^{-2}\ \mathrm{km}$ 附近，也就是 $70\ \mathrm{m}$ 左右，因此答案是(C)。&lt;/p&gt;
&lt;p&gt;過成看起來有些冗長，但其實精熟起來速度是很快的，甚至在別人下筆列橫式計算時，你已經秒殺這題了。&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>補習那些小事</title><link>https://dwye.dev/post/20140804_111118/</link><pubDate>Mon, 04 Aug 2014 11:11:18 +0800</pubDate><guid>https://dwye.dev/post/20140804_111118/</guid><description>
&lt;p&gt;一句話，補習&lt;strong&gt;千萬別盲補&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;高中教育中，若是一間師資正常的高中，理想上來看，學校教學基本上是足夠的。不過剛剛說的是「理想上」，若是真的不幸，在眾多老師裡面碰到了說話比較玄妙的老師，或是不幸地學校裡面沒有足夠的老師導致最後臨時找來一個沒什麼該科目教學經驗的，這時候再考慮以下補救之道：&lt;/p&gt;
&lt;h2 id="自修"&gt;自修&lt;/h2&gt;
&lt;p&gt;怎麼自修？應該用什麼教材來自修？自己自修讀得來嗎？這些都是決定自修之前必須評估的問題。以我看來，若不是對成績要求很大，基本上課本、習作、講義等組合應該夠用，甚至可能連講義都不需要。如果對成績有些要求，或是想更進一步學更多，可以買市面上的自修。像我在自修高中生物時，就是用翰林版的自修，個人認為效果不錯。&lt;/p&gt;
&lt;p&gt;自修的時候，通常會&lt;strong&gt;建議進度要比老師超前&lt;/strong&gt;這樣就算老師教的不怎麼好，在上到相關的進度時，至少心中有個譜，也會比較容易跟上老師的腳步，理解老師所教。&lt;br&gt;
當然，如果你那個科目很厲害，完全不用依靠老師，也可以拿上課時間來自習，無視老師教學，不過如果到時候考不好，下場請自負喔。&lt;/p&gt;
&lt;h2 id="補習"&gt;補習&lt;/h2&gt;
&lt;p&gt;如果覺得這一科不知道怎麼讀，或是已經自修過，發現自己並不是讀得很通，考試很危險，這時候再來考慮是不是要多上一次課。補習班的選擇這裡不多談，不過建議一定要試聽過，然後找到自己可以適應的老師，不管上課有什麼花招，老師觀念講得好是比較重要的。補習之後，還是要騰出時間自己來消化上課內容，&lt;strong&gt;很多東西不自己做一遍，是很難吸收進去的&lt;/strong&gt;在上課弄懂以後，要把東西變成自己的，考試才能進步。也因此，不建議花太多時間在補習上面，因為這樣自己念書的時間就會被壓縮太多。&lt;/p&gt;
&lt;h2 id="家教"&gt;家教&lt;/h2&gt;
&lt;p&gt;明明就有補習班存在，為什麼家教還有生存的空間？我一直認為家教是一個很特殊的存在，一直到大學我才接觸到這個行業。家教的特殊性質在於「&lt;strong&gt;客製化&lt;/strong&gt;，無論是學校，甚至是補習班，常常都是大班教學，無法顧及每個人，這時候若是有一個人可以專門帶領自己，解決難題，為自己設計一套適合的課程及規劃，也不失為一個好的選擇。&lt;/p&gt;
&lt;h2 id="其他資源"&gt;其他資源&lt;/h2&gt;
&lt;p&gt;如果你有一個好爸爸（誤XD），或是其他家人親戚兄弟姊妹懂得你不會的科目，多少可以向他們求助一些。如果你認識社團學長姐，或是班上有功課不錯的人，都可以向他們請教，教學的慾望相信每個人或多或少都有一些，你可以好好利用。&lt;/p&gt;
&lt;p&gt;綜合以上，補習並不是唯一選擇。&lt;/p&gt;
&lt;h2 id="補習學什麼"&gt;補習學什麼？&lt;/h2&gt;
&lt;p&gt;名師很多，總能把高中知識講的天花亂墜，什麼考題分析、趨勢猜測，題目分門別類，觀念總有獨門，秒殺速解、口訣速成，應有盡有。有時候補習班給了很多資源，可以就需要者使用之，不必全部拿來讀（像我當初補某家北車壽德大樓6樓補習班，就送了一堆書，他們大多還是全新，有些對於考試其實沒什麼助益，或是投資報酬率不夠。）這邊簡單列一下我們可以從補習班得到的資源：&lt;/p&gt;
&lt;h2 id="課程"&gt;課程&lt;/h2&gt;
&lt;p&gt;來補習班最重要的就是上課，把自己不懂的地方聽懂，知道一些解題技巧。這不多談。&lt;/p&gt;
&lt;h2 id="講義與書籍"&gt;講義與書籍&lt;/h2&gt;
&lt;p&gt;可以參考一下，補習班講義為何要這樣編排，是這樣學習比較有效果嗎？裡面的題目也能當作練習，省去額外找題目的麻煩。補習班的額外送書有時候也是有實用的，像是我使用了補習班送的學測4000單，以及狄克生片語，少買了幾本書。&lt;/p&gt;
&lt;h2 id="額外輔導"&gt;額外輔導&lt;/h2&gt;
&lt;p&gt;我的職業是輔導老師，就是專門在上課以外的時間幫學生解決學業上的疑惑，若是善用這項服務，也許可以幫自己節省一些念書時間，有點像是短時間的個人家教，不過通常品質沒那麼有保證（因為錢比較少&amp;hellip;）。對了，要適當的使用，別濫用，你不可能問題多到要每天見你的輔導老師。&lt;/p&gt;
&lt;h2 id="學習方法"&gt;學習方法&lt;/h2&gt;
&lt;p&gt;這才是我認為最重要的，然而也是最少人能領悟到的。我高二時因為數學讀不來所以去補習，之後就因為領悟到適合自己的讀書方式所以離開了。在補習班裡面，你會知道哪些題目是你一定要會的，哪些公式背起來哪些不用（當然，前提是老師教法也要正確），看看老師強調哪裡，為什麼有些地方老師會跳過。甚至偶爾也可以跟老師小聊一下，在該科目教學那麼久，老師一定有自己的一套心得。&lt;/p&gt;
&lt;h2 id="知識沒有速成"&gt;知識沒有速成&lt;/h2&gt;
&lt;p&gt;一分耕耘一分收穫，錯誤的耕耘方式，可以讓你得到經驗；正確的耕耘方式，可以讓你成功得到知識。補習班絕對不是拿來速成省時間用的，如果現在選擇速成，以後大考複習的時候就麻煩了。大考出題範圍廣、觀念多，難以速成，所以還是一步一腳印吧。&lt;strong&gt;補習班，是拿來補個人的不足之處的&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="作讀書計畫"&gt;作讀書計畫&lt;/h2&gt;
&lt;p&gt;複習時，讀書計畫對於自己的方向來說其實頗重要，建議每個人都要有自己的讀書計畫，可以只有大綱，但是還是要有。對於每一科，依據自己的情況，選用不同的讀書方式與時間分配。我建議，真的不足的，再排補習；就算補習，還是要排該科讀書計畫。可以問補習班老師的意見，但是不要絕對聽從，&lt;strong&gt;沒有什麼方式是適合每個人的&lt;/strong&gt;就連我這篇文章也是，要抱著質疑的心態閱讀。&lt;/p&gt;
&lt;h2 id="just-do-it"&gt;Just do it&lt;/h2&gt;
&lt;p&gt;對於是否補習感到迷惘？讀書計畫不知道要怎麼排？我覺得，與其浪費時間在那邊想破頭不知道該怎麼走，不如就衝一發吧。先試看看不補習可不可以，不行再換；先試看看用這樣的方式能不能念起來，不行再改。從暑假開始到學測，半年的準備期，說長不長，說短還真的不短，還是有一些容錯率的。不知道怎麼做的時候，記住這句話：&lt;strong&gt;先做了再說，Just do it！&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="後記"&gt;後記&lt;/h2&gt;
&lt;p&gt;這篇是關於補習的，其實也都是個人看法啦。寫到最後有點像是念書教戰守則，不過我每次提筆要寫一篇關於念書的文章時，都寫不出來，所以只好就每個主題來寫了。對於全科班我不熟悉，所以不多加評論，但是還是建議不要補太多習。希望能或多或少幫助到一點人。&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>學測自然科參考書推薦</title><link>https://dwye.dev/post/20140730_041700/</link><pubDate>Wed, 30 Jul 2014 04:17:00 +0800</pubDate><guid>https://dwye.dev/post/20140730_041700/</guid><description>
&lt;p&gt;這陣子剛好幫家教學生挑書&lt;br&gt;
又剛好碰到高中板友有問&lt;br&gt;
所以趁機獨立出來一篇文章&lt;/p&gt;
&lt;h2 id="挑選理由與方式"&gt;挑選理由與方式：&lt;/h2&gt;
&lt;p&gt;都是親自到書店看過&lt;br&gt;
以學測範圍為主&lt;br&gt;
盡量連社會組都適合使用&lt;br&gt;
重點除了排版、內容編寫，還有選題&lt;/p&gt;
&lt;h2 id="物理"&gt;物理：&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.sanmin.com.tw/product/index/005239583"&gt;全華－終極解碼物理學測複習&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="化學"&gt;化學：&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.pcstore.com.tw/ibookhouse/M14417912.htm"&gt;康寧泰順－引航高中化學總複習(學測篇)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.tameng.com.tw/front/bin/ptdetail.phtml?Part=NY06385&amp;Rcg=519"&gt;h南一－學測廣角鏡 化學&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="生物"&gt;生物：&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.wunanbooks.com.tw/product/9789862242179"&gt;建興－高中學測總複習上上籤 生物&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.pcstore.com.tw/ibookhouse/M10835609.htm"&gt;華逵－邏輯式學測生物總複習&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="地科"&gt;地科：&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.go8.com.tw/index.php?route=product/product&amp;path=&amp;product_id=4419"&gt;龍騰－地球科學嘿皮go&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.m.sanmin.com.tw/Product/index/99b155b8l109Z39Q105N66S107z125JIJgPSg191OzY"&gt;華逵－滿級分學測地球科學嘿皮書&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(別問為什麼物理只有一本XD)&lt;br&gt;
更多關於參考書，請見&lt;a href="../20140726_055506"&gt;此篇&lt;/a&gt;&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>【好站分享】邱博文物理線上資源</title><link>https://dwye.dev/post/20140729_060106/</link><pubDate>Tue, 29 Jul 2014 06:01:06 +0800</pubDate><guid>https://dwye.dev/post/20140729_060106/</guid><description>
&lt;p&gt;如果學物理碰到一個觀念，一直無法想像怎麼辦？&lt;br&gt;
要怎麼從死板板的圖片變成順暢的動畫在腦中播放？&lt;br&gt;
在這個數位化的時代&lt;br&gt;
其實網路上已經有很多資源可以幫助我們學習&lt;br&gt;
這邊要分享的就是其中之一&lt;/p&gt;
&lt;p&gt;邱博文老師，是現在台北的高中物理補教界名師&lt;br&gt;
除了曾在儒林、台大等補習班教書之外&lt;br&gt;
也擁有自己的公司、自己的補習班&lt;br&gt;
以及遠大的個人教學理念與抱負&lt;br&gt;
當然他也有建立很多的物理教學動畫&lt;br&gt;
其中包含歷屆物理試題解題影片、各章重點教學影片、物理3D模擬動畫&lt;br&gt;
好資源，不用嗎&lt;br&gt;
以下附上 youtube 網址&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.youtube.com/user/feynmantech/featured"&gt;http://www.youtube.com/user/feynmantech/featured&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;另外，這是費曼因公司的網站&lt;br&gt;
下載專區也有一些資源可以使用&lt;br&gt;
主要有用的集中在「3.學習資源」裡&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.e-physics.net/download.html"&gt;http://www.e-physics.net/download.html&lt;/a&gt;&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>我流挑參考書的方法</title><link>https://dwye.dev/post/20140726_055506/</link><pubDate>Sat, 26 Jul 2014 05:55:06 +0800</pubDate><guid>https://dwye.dev/post/20140726_055506/</guid><description>
&lt;p&gt;時間過得很快，課綱改了兩次，原本的文章參考書推薦部分似乎也不合時宜了，因此就順便把這篇文修改一下，希望有緣看到的人皆能受惠。&lt;/p&gt;
&lt;p&gt;市面上參考書林林總總，競爭非常激烈，而且由於賺頭不小，越來越多老師和出版社加入戰場。一般來說，學校老師都會推薦或是強制我們購買某一本參考書，而網路上也有不少推薦文（如：&lt;a href="http://stu60912.pixnet.net/blog/post/46054601"&gt;Treak參考書推薦&lt;/a&gt;）。不過我是個眼見為憑的人，寧願自己去書店花個一兩小時，自己挑選自己中意的參考書。&lt;/p&gt;
&lt;p&gt;對我來說，課本的功能只是查詢用，而複習時最重要的筆記等都會集中在第一階段地毯式複習的參考書（詳見下文）。換句話講，我是採用以參考書為中心進行複習的風格，所以參考書的挑選此時變得格外重要。&lt;/p&gt;
&lt;p&gt;其中我主要會參考的點有&lt;/p&gt;
&lt;h2 id="一印刷與排版"&gt;一、印刷與排版&lt;/h2&gt;
&lt;p&gt;每個人都有每個人習慣的版面，如果一本書的版面第一眼就讓自己看起來很不舒服，更不用談連續鑽研它好幾個月了。還有彩色這部分，一般來說生物、地理這兩科我會偏好選彩色講義，不過彩色上色也要上的好，不然太花了看久眼睛也會很痛，同時也比較難突顯出自己用筆做的重點。&lt;/p&gt;
&lt;h2 id="二文字敘述"&gt;二、文字敘述&lt;/h2&gt;
&lt;p&gt;有分「條列式」和「文章式」兩種，因為課本屬於後者，我比較偏好前者，同時重點式比較吸引人去看。當然其實大部分的參考書都是兩種綜合的只是比例不同。&lt;/p&gt;
&lt;h2 id="三例題解說與詳解"&gt;三、例題解說與詳解&lt;/h2&gt;
&lt;p&gt;一般來說我會選擇有逐題詳解的，因為我覺得這代表作者的用心程度，不過詳解內容還是稍微看一下，因為有的參考書的詳解只是重複答案的內容（ex:(A)(C)(D)正確。），而詳解多也不一定是好事，因為這樣你要找很久才能找到解題關鍵&amp;hellip;&amp;hellip;對我來說，最好的詳解應該是要「快、狠、準」的。&lt;/p&gt;
&lt;p&gt;至於有沒有例題解說我倒不太在意，因為我都是一樣把解說遮住當一般的練習題練習。&lt;/p&gt;
&lt;h2 id="四編寫內容"&gt;四、編寫內容&lt;/h2&gt;
&lt;p&gt;大考考的不外乎是基礎知識跟常識，尤其學測更偏向這個方面。所以說，太刁鑽的題目、敘述基本上都免了，以多講觀念、多做觀念釐清的參考書為佳，如果有有趣的課外補充可以參考一下，只是命中考題的機率真的頗低，常識的部分還是平常就要多關心科學知識比較實在。&lt;/p&gt;
&lt;h2 id="五出版社作者"&gt;五、出版社、作者&lt;/h2&gt;
&lt;p&gt;比較大的出版社有翰林、華逵、晟景&amp;hellip;&amp;hellip;等等，通常大出版社的書都比較齊全，但有時候為了完整性每一科都有出參考書，只是有些科目的品質就比較差強人意。一般而言市面上比較有名的有：翰林的大滿貫與學測新導向系列、華逵的生物精通以及地科嘿皮書、晟景的複習週記和平安符系列、龍騰的社會科滿分攻略、三民的英文套書等。有的小一點的出版社也有有名的書如：康寧泰順書坊的引航化學、藏經閣的九陰真經等。&lt;/p&gt;
&lt;p&gt;作者部分很多補教名師和明星高中的老師都會出書，像是我們附中還有建中的老師都是出了名的愛出書的。同時也有不少補教老師會把自己多年的教學經驗和講義寫成參考書出版。我一般來說會選擇比較有名的老師寫的書，因為有名代表那位老師是真正有這方面的教學專長的，也是一種保障。不過，不一定準。&lt;/p&gt;
&lt;h2 id="六更新日期"&gt;六、更新日期&lt;/h2&gt;
&lt;p&gt;一般來說，用心的作者會每年把最新考題還有趨勢更新在書裡面，以及改正之前的謬誤，也是一個參考的點。通常大出版社的書會比較常更新，不過有的書更新了其實只是加入新的一年考題還有改一下出版日期而已。其實若不是歷屆試題本，我覺得只要課綱符合即可，差個一兩年不會影響太多，反正新的歷屆試題之後也都有機會再做。&lt;/p&gt;
&lt;h2 id="七其它人的評價"&gt;七、其它人的評價&lt;/h2&gt;
&lt;p&gt;學校老師通常對於自己科目的參考書有一套看法，可以多和老師聊聊天。網路上也有不少關於參考書的評價，其中像是深藍、PTT高中版等都有不少討論串可以參考。當然也可以向其他同學或是學長姊請教。&lt;/p&gt;
&lt;h2 id="參考書使用規劃"&gt;參考書使用規劃&lt;/h2&gt;
&lt;p&gt;以學測來說，我的複習方式主要分成三階段：「地毯式補坑複習」、「快速重點複習」、「考前衝刺」。&lt;/p&gt;
&lt;h2 id="一地毯式補坑複習"&gt;一、地毯式補坑複習&lt;/h2&gt;
&lt;p&gt;這個階段通常在暑假就會進行，我會選用比較深入詳盡的參考書，扎實的把整個學測範圍看過一遍，把之前高一高二留的坑洞盡量補起。這個階段推薦的參考書類型像是翰林的大滿貫這類，也許東西會太多，這時候就要畫記重點，並把自己不熟的地方記著，以便接下來再複習。&lt;/p&gt;
&lt;h2 id="二快速重點複習"&gt;二、快速重點複習&lt;/h2&gt;
&lt;p&gt;開學以後，要兼顧進度以及學測，可能就沒辦法挪出那麼多時間複習，這時有可以把第一次複習時的重點以及不熟的地方快速掃過，配合分範圍的題庫做複習。或是可以再買一本偏向重點式的參考書（如平安符系列等）。&lt;/p&gt;
&lt;h2 id="三考前衝刺"&gt;三、考前衝刺&lt;/h2&gt;
&lt;p&gt;理想狀況是，考完最後一次模考到學測前，已經完成前面兩個步驟，接著就以歷屆試題和模擬試題交叉練習，從題目找出不熟的地方，也要弄清楚歷屆題目的來龍去脈。&lt;/p&gt;
&lt;h2 id="工商時間"&gt;工商時間&lt;/h2&gt;
&lt;p&gt;這邊簡單推薦幾本物理參考書，是我認識的老師所寫，我也親自看過。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.wunanbooks.com.tw/product/9789572185643"&gt;全華－終極解碼物理學測複習&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一本題目挑選OK，觀念和例題講解都清楚的書。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.m.sanmin.com.tw/Product/Index/004402997"&gt;全華－優勢高中基礎物理(一)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;就是本自修，學測主要考的內容不會比基礎物理(一)多出太多，用自修札札實實重新念一遍也是不錯的選擇。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.m.sanmin.com.tw/Product/Index/003011633"&gt;龍騰－稱霸高中物理學測總複習講義&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;這本書補充比較多，也有配合邱博文老師一直再推廣的網路學習資源，適合自然組使用，社會組的話後面題目有些比較難可以跳過。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.m.sanmin.com.tw/Product/Index/003773534"&gt;龍騰－稱霸高中物理指考總複習講義&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一本詳盡的高中物理全範圍複習書，除了觀念提示之外，每章後面也會幫你做好重點中的重點，選題也不錯。&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>【高中科目心得】兩年後，再看高中物理</title><link>https://dwye.dev/post/20140725_112921/</link><pubDate>Fri, 25 Jul 2014 11:29:21 +0800</pubDate><guid>https://dwye.dev/post/20140725_112921/</guid><description>
&lt;h2 id="緣起"&gt;緣起&lt;/h2&gt;
&lt;p&gt;從一個學生，到進入物理家教界兩年，以及在補習班工作，對於高中物理的認知，看法未必與以前完全相同，現在從一份半個教育者的眼光來看高中物理，所能看到的應該會比以前更廣更全面。&lt;/p&gt;
&lt;h2 id="走上物理家教"&gt;走上物理家教&lt;/h2&gt;
&lt;p&gt;會開始走上這條路，一切都是機緣。原先只是抱著累積經驗的心態，沒想到教著教著，卻也教出興趣來。那時候還自己用電腦把要教的題目全部重新打一次，集中在一張紙上，一開始當然也是教的有點抖抖的，所以比較難一點的題目我都要偷偷把解法抄在筆記本裡面，臨場緊張忘記的時候，才可以抱一下佛腳。&lt;/p&gt;
&lt;p&gt;後來隨著時間的推演，應徵進了補習班解題，對於我的臨場物理實力是一大幫助。同時我也接了第二個學生，我還是很堅持每次都要自己整理題目，並且用一本小筆記本寫下這次要教的東西當小抄，不用參考書和學生講義上課，自成一個脈絡。當然對於還有學校課業和社團要忙得我來說，兩個家教似乎是不小的負擔，尤其學生進度又不一樣，常常在家教的前一天晚上，都會打題目到半夜兩三點。但是再怎麼累，只要第一次撐過去，之後我就可以繼續用這些題目來教，以後接新的家教就能如魚得水，省下不少的力。&lt;/p&gt;
&lt;p&gt;只是，凡事都不會那麼順利，在第二年尋找新的家教 case 上，因為競爭者眾多且僧多粥少，實在花了不少精力才找到 case。而且因為第一年教的兩個學生都屬於很聰明的類型，學習和吸收速度都十分優秀，突然教到一般高中的學生，會有點適應不上來，變成我要花更多時間慢慢講解題目，題目的挑選也不能太難。原先我計畫著同一份題目一直沿用，最後也只能量身訂做。&lt;/p&gt;
&lt;h2 id="補習班見聞"&gt;補習班見聞&lt;/h2&gt;
&lt;p&gt;自從進了補習班之後，我對於物理教學真是「大開眼界」，補習班裡面有各種程度的學生以及班級，有各種不同教法的名師，以及天花滿天墜的帥氣解法和公式等等。我在補習班這些日子，實力真的上升了不少，同時對於每道題目幾乎都可以有兩種以上的解法。在補習班，也許有不少人不能認同老師上課分題型，學秒殺法的方式，但仔細想想，老師一人要對付學生數百人，能找到所有學生都滿意的教學方式，還真不容易。我覺得，老師要學生背一些解法，學一些偏門觀念，不也實質對於學生的段考和模考是有幫助的？認同的人就學起來，不認同的人也大可用自己的解法罷了。&lt;/p&gt;
&lt;h2 id="學物理的捷徑"&gt;學物理的捷徑&lt;/h2&gt;
&lt;p&gt;學物理的捷徑，可想而知，就是：「&lt;strong&gt;走大路不抄小路&lt;/strong&gt;」小路看起來雖然快，但難免曲折，容易迷失在裡面。隨著看過的學生越來越多，其實很多學生的問題源都是「只領悟到觀念的碎片，而遺忘了完整的觀念」。教育部課綱其實把每個章節的大重點以及學生該學什麼都寫的一清二楚，也有強調哪些東西學生要學計算，哪些東西不提推導僅介紹，哪些東西只作定性描述等等。不過，市面上少數講義內容從聯考時代改改改，卻忘了參照課綱，導致收進了一些其實大考根本不強調的東西。學校段考也常如此，但就讓學生增廣見聞的理由來說，還情有可原，只是我覺得學生應該要知道哪些東西是「基礎」，哪些東西是「旁支」，哪些東西是「看看就好根本不用會」。也許第一次學，難免囫圇吞棗，但在複習的時候，我會希望學生能做到這些。&lt;/p&gt;
&lt;h2 id="學測物理騙局"&gt;學測物理騙局&lt;/h2&gt;
&lt;p&gt;拿起新課綱的北模自然科試題，整理出物理的部分，大部分的題型都能分入下面兩類：計算、是非。可以考計算的，就出個物理計算題。科學家和強力量子論等不能考計算的，就考是非。學測固然有這些題型，但並不是最主要的部分，尤其是看過第二部分考題就知道，大多都是以閱讀引導作答，很少有必須完全靠自己的知識作答的題目。但這也不能怪出題者，畢竟學測是耗資不小，經歷反複檢驗的題目，題形嚴謹卻能有變化，能真正測驗學生的程度而不是死背，相形之下，模考常常都是老師必須額外挪出時間出題，誰不會想偶爾改個考古題丟進去了事？倒也因為這樣，許多自然科不錯的人都會告訴我們「物理不用讀」，這麼說似乎有點誇張，但若國中有打好基礎，學測範圍的物理幾乎沒有多出太多東西，反而關鍵性的題目常常都是那些閱讀題，那是一般老師很難教導給學生的。所能靠的，就是學生平常培養眼界，多接觸科普知識，當然基礎觀念一定要滾瓜爛熟，才能有在題目中找出線索的能力。&lt;/p&gt;
&lt;p&gt;除了模考之外，**連歷屆試題都會騙你！**最近的課綱修改為例，其實刪去的不少東西，例如說「家庭用電」，原本是95課綱的一大考點，99課綱中這個章節完全被砍去。所以若是有人做到類似題目，分不清火線地線，以為自己高一沒有認真學習，這就錯了。因為，高一根本沒教你這些東西。至少以物理科來說，做歷屆試題時，因為課綱變化不小，要自己注意到哪些已經在課綱範圍外，最好的方式就是：把課綱也看一遍。照著課綱去找觀念，絕對不會漏，也可以避免多讀，能把省下的時間去顧其他科，也是不錯。&lt;/p&gt;
&lt;h2 id="國中撐起高中"&gt;國中撐起高中&lt;/h2&gt;
&lt;p&gt;因為我手邊也正且有國中會考的理化複習講義，翻了翻，才察覺到，國中範圍跟高一物理的重疊性實在不小，諸如打點計時器、電與磁、作功等等&amp;hellip;&amp;hellip;，其實很多事情都是國中講過的，高一物理只重觀念介紹，也不會有太多複雜題型。簡單說來，只要國中學好，高一物理根本輕鬆學！反過來說，其實很多物理不好的人，就是因為從國中開始，基礎都沒打好，才會一路爛下去。這也顯示出家教時因材施教的重要性，若是國中基礎有顧好（因為基測制度，這些人大多在明星高中，我相信在會考以及特招存在的情況下，未來仍然是這樣），那麼高二之後的物理便能省一點力，簡單介紹觀念後就能直接進入題目分析與計算。若是國中基礎沒打好，那麼觀念部分就絕對不能放水，還要配合國中等級的簡單題目，先把觀念搞懂來，才能接下去處理高二高三的題目。這樣一來，若是以家教為例，這類型學生就必須多花一些時間來教學，所以可能一堂課就要三小時以上，或是每週多上一次課程，才不會教學時間捉襟見肘。&lt;/p&gt;
&lt;h2 id="會與熟練"&gt;「會」與「熟練」&lt;/h2&gt;
&lt;p&gt;每年考試，都會有人在ptt馬後炮：「這明明我就會啊，怎麼當時沒想出來」「這題我有想到解法，可惜時間不夠」。不管是學測自然或指考物理，考場上我們就是和時間賽跑。把時間除以題目數，指考每題大約有3分鐘的作答時間，學測每題可以分到的作答時間更不到2分鐘。更何況，一般人推崇的先寫完、後檢查原則，代表著寫完之後還要留下時間檢查，所以每題可以分到的作答時間就更少，也因此熟練度的鍛鍊也是很重要的。&lt;/p&gt;
&lt;p&gt;現在不是談大學知識，是談高中考試，&lt;strong&gt;看到題目要「會」，知道題目在考哪裡，只是基本功&lt;/strong&gt;理想的狀況，是看到題目不只是要會，更要浮現「切入方法」，也就是對於觀念以及解題的熟練度要夠。尤其是指考，物理不是一科用「讀」的科目，是要用「算」的。其實這對於段考也適用，在唸物理時，希望學生不只是能知道觀念，更要時常把觀念「用」在題目裡面。同時，能把做過的題目訂正，想不出來的題目，不是背誦祥解，而是知道為什麼詳解如此切入。別人寫的出來，沒道理自己不行，只是自己的功夫還沒到家，所以才需要多練習，練習看到題目能知道問題點在哪以及切入方法，在考場上才能及時作答，達到快狠準的境界。&lt;/p&gt;
&lt;h2 id="為什麼要學物理"&gt;為什麼要學物理&lt;/h2&gt;
&lt;p&gt;「物理，即事物之道理，於世間萬物之中，尋找共同之規律。」一般人學物理，都是在進學校後，一本自然課本發現，就不知不覺被老師帶入物理的世界裡。當然，有的人僅初窺皮毛，有的人樂於其中並向前探索，甚至有的人走到尖端，撐起這個世界。日常生活中，有多少現象和物理有關？其實國中物理所學，幾乎都是常識，例如光的性質、聲音與速度等等，這些是天天都會在身活周遭用到的。例如開車時候，怎麼看自己和前面的車會不會相撞；行人要走多快，才能避開遠方行駛而來的車，這些事情，我們不一定動筆算，但學過物理的我們，腦中其實已經自動幫我們算出大略的答案。同樣道理，為什麼保險絲會燒掉，為什麼我們不能把電線挖開讓電器全部串來在一起使用，腳踏車出問題要怎麼找問題點&amp;hellip;&amp;hellip;，還有更多事情，都和物理有關。也許我們不用會計算，但是物理的觀念，多少知道一些，對於生活也是有所幫助的。也因此，高中就分為自然組和社會組，社會組就不用學太多題目的計算，只要知道基本觀念就夠了。&lt;/p&gt;
&lt;h2 id="物理與化學"&gt;物理與化學&lt;/h2&gt;
&lt;p&gt;一定有人會說，我是化學系，與物理有何相干？為何一個化學系的人，敢跑來物理家教市場搶我們物理人的飯碗？其實，若熟讀科學歷史，不難發現，很多時候物理學家和化學家是難以區分的。有句話說：「&lt;strong&gt;數學，是應用哲學；物理，是應用數學；化學，是應用物理；生物；是應用化學&lt;/strong&gt;」沒錯，化學身為研究變化之學問，怎麼能夠和事物的道理切分開來？物理的原則，總不會到了化學課本裡面就不適用了吧？量子力學的發展，導致了原子軌域觀念的產生，很多人以為，化學就是一門在實驗裡面觀察並歸納的學問，其實很多時候，化學也是「算」出來的。以我現在待的理論化學組來說，理論化學實驗室是所謂「dry lab」，沒有藥品的實驗室。沒有藥品怎麼做實驗？我們用電腦模擬實驗，那電腦要怎麼建構出化學機制？當然是用已知的「物理」原則建立模型，蓋出一個世界。其實理論化學，現在常常跟物理化學掛在一起，幾乎可以算是物理化學的一塊，所靠的就是近代物理最大的突破：量子物理（或在化學界，有個科目叫做量子化學，便是將量子力學的一些原則用於化學上）。物理與化學，同屬於科學，在某些領域上自然難以切割，正如生物與化學也常常有所交集一般。&lt;/p&gt;
&lt;h2 id="從高中物理到大學物理"&gt;從高中物理到大學物理&lt;/h2&gt;
&lt;p&gt;每當大學放榜，總是有許多新鮮人迫不及待想要先看看大學的世界長什麼樣子。就我看來，大學理科最重要的科目，非「微積分」莫屬了。大學普物，以知名課本 Halliday 所著之 Fundamentals of Physics 為例，除了會把高中的基礎幾乎全部講一遍之外，也把數學工具，就是所謂微積分，活用於其中，用同樣的觀念就可以解出更多高中所不能解決的問題。當然也有一些新的領域進來，例如流體、熱學、原子核等等。電磁學更是把不得不提的超知名物理明星馬克士威所整理出的四大方程都介紹一遍，這是沒有微積分基礎的高中生所無法觸及的。在普物之後，以物理系而言，會就各個領域規劃更進階一點的課程，例如「力學」、「電磁學」、「熱物理」、「量子物理」等等，了解物理學各界的發展，讓學生置身歷史之中，了解學問的來龍去脈。當然，若是要濟身於物理界的尖端，我甚至可以說，照著課表修課是絕對不夠的，就算到了研究所，學到了「量子場論」、「進階電磁學」之類的課程，也都是近代早有不少先驅在裡面探索的領域，這時候若是直接訪問教授的研究，甚至進到實驗室進行專題研究，反而比較能接觸到學問的尖端。不過，這必須建立在廣大的先備知識下。物理的領域博大精深，高中物理，恐怕連皮毛都稱不上，因此也不必抱怨高中教太多東西，因為從大學的角度看來，高中，根本沒教什麼東西。&lt;/p&gt;
&lt;h2 id="還有更多"&gt;還有更多&lt;/h2&gt;
&lt;p&gt;上一篇「&lt;a href="../20120711_061900"&gt;最弱科到最強科，我的物理 by YDW&lt;/a&gt;」其實也分享了不少東西，關於讀書的方式，也提及不少，裡頭所寫過的，就避免再寫一次。這篇文章說實在，也沒多給出太多東西，大概就是統整這兩年來的所見所聞吧。見到的學生越多，領悟的也越多。只希望我碰到的人，不一定要喜歡物理，只是不要以物理為苦惱，因為，讓物理變的討人厭的絕對不是物理本身，而是教育制度和考試。也許未來，還會有第三篇或更多篇，但我接下來想做的，是把分雜在各個地方的經驗以及知識，寫成一小篇一小篇的專題，不求很多人看過，但求看過的人皆能有所收穫，如此而已。&lt;/p&gt;
&lt;p&gt;by YDW 2014.07.25&lt;/p&gt;
&lt;h3 id="補充"&gt;補充&lt;/h3&gt;
&lt;p&gt;想知道理論化學在幹嘛的，可以參考鄭原忠老師的線上課程（當然，需要有大學普通物理、普通化學與微積分作為基礎）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://quantum.ch.ntu.edu.tw/online_courses/index.html"&gt;鄭原忠 OCW: Quantum Dynamic and Spectroscopy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>【文章推薦】申請入學：面試與備審</title><link>https://dwye.dev/post/20140725_064404/</link><pubDate>Fri, 25 Jul 2014 06:44:04 +0800</pubDate><guid>https://dwye.dev/post/20140725_064404/</guid><description>
&lt;p&gt;一篇台大教授關於面試學生的心得文與建議&lt;/p&gt;
&lt;p&gt;我想，從教授的角度直接分析&lt;/p&gt;
&lt;p&gt;應當比補習班旁敲側推來的有用多&lt;/p&gt;
&lt;p&gt;&lt;a href="http://chernyr.blogspot.tw/2012/04/blog-post.html"&gt;文章連結&lt;/a&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;a href="http://www.books.com.tw/products/0010542007"&gt;連結&lt;/a&gt;&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>對於公式的態度</title><link>https://dwye.dev/post/%E5%B0%8D%E6%96%BC%E5%85%AC%E5%BC%8F%E7%9A%84%E6%85%8B%E5%BA%A6/</link><pubDate>Thu, 17 Jul 2014 17:28:48 +0800</pubDate><guid>https://dwye.dev/post/%E5%B0%8D%E6%96%BC%E5%85%AC%E5%BC%8F%E7%9A%84%E6%85%8B%E5%BA%A6/</guid><description>
&lt;p&gt;相較於指考，學測物理有的公式實在不算多，而且課綱中都講明了，很多章節都已經避開推導以及計算。因此剩下的公式，大多算是基本中的基本，例如牛頓第二運動定律 $F = ma$，相信大家國中也都背過了。但有些公式就複雜多了，例如阿特午機(Atwood machine)的張力公式：&lt;br&gt;
$$&lt;br&gt;
T = \frac{2m_1m_2}{m_1+m_2}g&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;這樣複雜的公式，也許有人根本沒看過，也許有人在學校課堂或補習班講義裡面看過它，這種公式到底要不要背？&lt;/p&gt;
&lt;h2 id="用最少的公式解最多的題目"&gt;用最少的公式，解最多的題目&lt;/h2&gt;
&lt;p&gt;我的看法是，必須把公式分成兩類，一類是「&lt;strong&gt;物理定義&lt;/strong&gt;」，像是 $F = ma$，你不背起來，根本沒辦法從受力算出加速度。另一類是「&lt;strong&gt;導出公式&lt;/strong&gt;」，像我剛剛舉的這個例子就是，這種公式不背也沒差，背了是增加解題速度，但缺點常常是使用範圍太窄，例如這個公式只能用在阿特午機，段考還好，但大考幾乎沒有投資報酬率可言。這類型的公式要不要背，完全看個人，但我的建議是能不背，就不背，&lt;strong&gt;用最少的公式，解最多的題目&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="公式的意義"&gt;公式的意義&lt;/h2&gt;
&lt;p&gt;當我們背下每條公式時，應該要知道&lt;strong&gt;每條公式背後的物理意義&lt;/strong&gt;，如果是導出公式，更要知道公式是怎麼推導出來的。碰到題目時，先分析題目要什麼，我們有什麼條件，這些條件和答案有什麼公式可以帶出，例如說等加速度運動，給初速度、末速度、位移，要求加速度，熟練的人就會立刻想到$v^2=v_0^2+2a\Delta x$，當然畫 $v$-$t$ 圖也是一種解法。腦袋裡面先有流程再動筆是最好的，若是想不到，也可利用起始條件，走一步算一步。&lt;/p&gt;
&lt;p&gt;當然，如果只是考學測，幾乎沒有複雜的計算及推導，剩下少少的公式幾乎國中都背過，似乎也就沒那麼多煩惱了，只要把公式背後的物理意義搞懂即可。&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>我怎麼唸英文</title><link>https://dwye.dev/post/%E6%80%8E%E9%BA%BC%E5%94%B8%E8%8B%B1%E6%96%87/</link><pubDate>Sat, 14 Jul 2012 22:02:18 +0800</pubDate><guid>https://dwye.dev/post/%E6%80%8E%E9%BA%BC%E5%94%B8%E8%8B%B1%E6%96%87/</guid><description>
&lt;h2 id="背景"&gt;背景&lt;/h2&gt;
&lt;p&gt;　　先說說我高一時的狀況吧，就是一個標準的混混學生，英文雜誌當擺飾，只有在考前才會拿出來惡補，國中也只有1000單的能力。上了高中在第一、二次段考的成績不是很滿意所以就去補習了。&lt;/p&gt;
&lt;h2 id="補英文的歷程"&gt;補英文的歷程&lt;/h2&gt;
&lt;p&gt;　　其實我只被同學拉去試聽過劉毅英文而已就去補了，當時選的原因只是因為基測ＰＲ夠高可以減免很多學費，不過後來又繼續待在裡面一直到高三上。我試聽的是後來紅極一時的陳子璇老師的課，當然之後報名也是由她上課。&lt;/p&gt;
&lt;p&gt;　　先說說我在劉毅英文的心得吧，首先他們的上課特色就是大量歸納同義字、同義片語，如果是版本班的話基本上應付學校段考是沒有問題的，不過在高二時就全部轉報模考班（好像還是有附贈版本班可是我那時太忙了沒去聽）。&lt;/p&gt;
&lt;p&gt;　　劉毅模考班的特色就是難度相當暴力，高二剛開始可能會非常不習慣，上課速度也很快並且補充很多東西，上課講義印的很詳細。雖然難度有點過頭，不過在訓練自己寫選擇題的功效上還有一些同義字的歸納記憶上來說是蠻有效的，我的英文也是這樣穩穩的進步到現在這個程度的。&lt;/p&gt;
&lt;h2 id="在補習之後"&gt;在補習之後&lt;/h2&gt;
&lt;p&gt;　　補了兩年的英文，會不會後悔呢？我覺得補習不只是把補習班所教的知識吸收進去，更是要把補習班的「教學方法」也跟著吸收走，所謂「教學方法」其實也是我們之後尋找自己適合的學習方法的一種參考。向劉毅英文就很強調「單字量的累積」和「每周一次模考題練習」，而我再沒有補習之後也是照這個方式繼續增強自己的英文實力的。我是覺得，如果自己讀真的沒辦法再去補習，不然我還是會先嘗試自己讀為主。&lt;/p&gt;
&lt;h2 id="所以那個學習方法"&gt;所以那個學習方法？&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;單字&lt;/li&gt;
&lt;li&gt;文法/句型&lt;/li&gt;
&lt;li&gt;閱讀&lt;/li&gt;
&lt;li&gt;寫作&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="單字"&gt;單字&lt;/h3&gt;
&lt;p&gt;我會用空閒時間增加單字量，例如說買午餐排隊的時候，或是在車上無聊的時候，隨身攜帶4000單/7000單是我當時的特色之一。當時選擇了三民書局出版的隨身單字本。不過補習班也有發4000單，其實有很多選擇，就看哪本最順眼吧。不過，&lt;strong&gt;一定要有例句&lt;/strong&gt;！這樣才能了解單字的用法，並幫助記憶！而且我會挑對於一個單字，能夠註明多種意思的書，畢竟外文跟中文的發展歷史不同，意思不可能是一對一的。&lt;/p&gt;
&lt;h3 id="文法句型"&gt;文法句型&lt;/h3&gt;
&lt;p&gt;我是跟著學校使用康熹出版的《必背英文句型》，這本書的特色就是簡單明瞭，分章很有規則，一頁就是一個句型，很適合拿來規劃複習。但我寫完一本覺得不夠熟係，又挖出了以前學校訂英文講義附送的句型本，但這本就因為時間的關係沒有寫完。&lt;/p&gt;
&lt;h3 id="閱讀"&gt;閱讀&lt;/h3&gt;
&lt;p&gt;加強閱讀的方法有很多種，包含英文雜誌、看英文新聞、直接多做閱讀題等等，我是選擇直接多寫模考題解決，模考題的來源主要是學校，另外我也有自己購買歷屆試題把它寫完並檢討完，對我而言這樣就已經足夠了。&lt;/p&gt;
&lt;h3 id="寫作"&gt;寫作&lt;/h3&gt;
&lt;p&gt;沒有什麼訣竅，就單純多寫，每個禮拜寫兩篇拿給老師改（不過前提是老師願意配合，也很謝謝我們學校的英文老師，多半都很熱心，鼓勵我們寫作文給他們改），並且取得老師的意見，每次不斷的改善。&lt;/p&gt;
&lt;p&gt;也可以在寫作時練習最近讀到的文法與單字，可以加深印象之外，也能增加自己的寫作詞庫。&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>[高中科目心得] 最弱科到最強科，我的物理</title><link>https://dwye.dev/post/20120711_061900/</link><pubDate>Wed, 11 Jul 2012 06:19:00 +0800</pubDate><guid>https://dwye.dev/post/20120711_061900/</guid><description>
&lt;h2 id="寫在最前面"&gt;寫在最前面&lt;/h2&gt;
&lt;p&gt;我覺得，物理是一科非常吃「讀書方法」的科目，好的讀書方法帶你上天堂，不好的讀書方法帶你到懷寧街某獨棟補習班。一個單元中哪些公式一定要背，哪些觀念要如何釐清，哪些解法是要知道的，在考前一定要掌握這些脈絡，考試時才能從容地正確作答。&lt;/p&gt;
&lt;h2 id="全新的挑戰"&gt;全新的挑戰&lt;/h2&gt;
&lt;p&gt;高一的時候我們班老師是名聲傳遍千里的Ｐ哥，三次段考都是走標準的Ｐ哥簡單風（兩張單面，一題五分，共二十題，全部都是習作和講義的題目&amp;hellip;&amp;hellip;我們這屆的人應該在熟悉不過了），因此到了高二其實有很大的不習慣，第一堂課就被茂福的微積分嚇著了！其實說來也頗貼心，為了因應Ｐ哥基礎物理和真正高二物理的差距，茂福補充了不少資料給我們，不過那個微積分證明當時我可是一點也看不懂&amp;hellip;&amp;hellip;突然面對那麼多符號總是會讓人不甚習慣。&lt;/p&gt;
&lt;p&gt;沒錯，進入了高中物理的世界，有很多事情是需要適應的。其一便是符號的運算，什麼 F,m,v,a,g,h,R&amp;hellip;&amp;hellip;..，不再像是國中的時候只是單純的數字運算，高中物理甚至會出現整題只有符號在運算的題目！千萬不要怕，給自己足夠的計算空間，只要有足夠的自信，其實列出式子後要做的只是移項一下答案就出來了（不得不說，我一直到高三時才克服這項恐懼&amp;hellip;&amp;hellip;尤其是波爾氫原子模型那邊）。另外，讀懂題目並以適當的圖解畫出題意也是重要的一項技能，像是 v-t 圖，還有茂福非常強調的力圖，這些圖形也是很吃空間的，所以說我覺得物理需要很大很大的計算空間，也難怪每次陸董上課都需要規劃好一整面黑板該如何使用，並且花一段時間畫圖。&lt;/p&gt;
&lt;h2 id="哪些公式該背"&gt;哪些公式該背？&lt;/h2&gt;
&lt;p&gt;大家都知道我是個討厭背公式的人，但是我還是有背一些公式，除了 F = ma、P = mv 這種近乎基本的東西之外，像是直線運動三大公式、微分公式、拉密定律（其實我背的是正弦）這些我有背下，那至於一些複雜的像是斜拋的公式還有阿特午機之類的我就以推導為主。有一些投資報酬率高的速算法，像是彈性碰撞的 V’ = 2Vc-V0 我就有背（背下這條也不需要花多大的力氣）。掌握一個原則，能推導的就以推導為主。背下每一個公式之前要知道那些公式是怎麼來的，還有甚麼時候可以使用那些已經背下來的公式，用投資報酬率的方式去衡量也是一招。至於如果需要幫助記憶的方法，可以上潘冠錡老師的網站，裡面有很多有用的東西，其中我強力推薦「圓周運動需要什麼力」和「SHM 是簡諧運動」這兩首歌，當然裡面還有其他的東西，有興趣的人就自己去挖寶吧！&lt;/p&gt;
&lt;h2 id="善用想像"&gt;善用想像&lt;/h2&gt;
&lt;p&gt;有些物理的概念其實是有些抽象的，因為日常生活中其實不太會碰到（進行了太多假設），像是功與能這章，當然還有克卜勒三大定律、靜電學、氣體動力論&amp;hellip;&amp;hellip;等等。想辦法習慣物體裡面有著「能量」這樣一個虛構的東西存在其中（還是你把它想成查克拉也可以&amp;hellip;&amp;hellip;）然後可能往下掉的時候那個物體內在的能量就會轉變成物體的動能，推動一個物體的同時也給予它能量等等，甚至和力學結合在一起！根據陸董所說，高中物理有三大守恆是很有用的，其一是「動量守恆」，其二是「角動量守恆」，另一個就是「力學能守恆」，只要在沒有非保守力作功和能量散失的情況下，就勇敢地用下力學能守恆吧！既然提到了守恆就在順便一提動量的重要性，基本上在高中物理會碰到的題目中，動量守恆是恆成立的，就算是核反應亦然！從碰撞一直到康普頓效應，動量守恆都是一項解題的利器，務必熟悉！&lt;/p&gt;
&lt;h2 id="物理好像有很多黃皮綠皮之類的書要看嗎"&gt;物理好像有很多黃皮綠皮之類的書，要看嗎？&lt;/h2&gt;
&lt;p&gt;對於高二進度這邊，我比較不能提供什麼經驗，原因很簡單，因為我的高二物理成績是徘徊在及格邊緣的。順便爆一下自己的料，我常常早上在物理課看報紙，而且沒有被茂福抓到過（不然可能就會&amp;hellip;&amp;hellip;茂福神掌！）。題目的話，除了學校採用的那本陳忠誠老師所編寫的講義之外，出版社送的講義也不會差到哪裡去（我們當時是用南一版），順帶一提，南一版的講義編寫者湯烈漢老師也寫了不少總複習參考書，大多是華揆出版。我主要是寫學校發的那本講義（換上了黃皮&amp;hellip;&amp;hellip;貌似是想沾沾張鎮麟黃皮書的名號），其實這樣就已經足夠了，但寫的時候重點是要把握每一題裡面所用到的觀念，其實很多題目是可以一以貫之的，注意別把觀念打碎了而執著於記憶每種題型的解法，我覺得我們要做的是讓自己的觀念穩固，在面對全新的題目時仍能知道如何突破。&lt;/p&gt;
&lt;p&gt;回到副標，要不要買這些書呢，我覺得，如果有意進修，這套系列的書的確是可以提供不少幫助，黃皮書主要用於扎實自己的基本觀念（兩本上下），綠皮書則是鑑往知來、修練自我的好工具（三本上中下，收錄非常豐富的題目，都頗厚），至於另一本藍皮書則是如果有對於實驗題型的需求時倒是蠻好用的，尤其是今年指考莫名其妙叫我們自己設計一道實驗，若是對於實驗不夠熟，更不用提如何設計一套新實驗了。但是缺點，同時也是這系列書的優點：東西太多了。尤其是綠皮書，若不是從高二就更著進度寫，對於我這個有六科要兼顧的三類組來說，怎麼可能寫得完？但若是以寶典方式來使用（也就是僅供讀寫重點和查詢用），也不失為不錯的方式，只是說真的，沒有必要，只給將來有興趣進修物理的人做做自我挑戰吧。&lt;/p&gt;
&lt;h2 id="學測物理怎麼辦"&gt;學測物理怎麼辦&lt;/h2&gt;
&lt;p&gt;承第二段，在Ｐ哥的帶領下我可是扎扎實實的讓自己的物理睡了一學期&amp;hellip;&amp;hellip;。在升高三暑假決定開始讀學測時，物理也是被我排在最不重要的科目，首先，物理只佔 1/4 科的重要度，再者，九五課綱的基礎物理基本上幾乎就是國中三年的物理再加上一點常識，對於小時候對於科學就有興趣的我，這根本不算什麼。此外，對於我們自然組來說，學過了高二物理，高一物理簡直是沒有難度可言，而學測高二物理又只考皮毛中的皮毛，甚至只需要在提供的文章中搜索推敲答案就呼之欲出，而且還不用全部答對（當然，全部答對最好，我曾經有「第二部分神話」，即是第二部分從來沒被扣到分過，每次都錯四題以內，只是剛好在101學測那一次打破，有點諷刺）。&lt;/p&gt;
&lt;p&gt;講了那麼多好像物理根本不用讀一樣，事實上我還是自己挑了一本參考書。我們班大部分的人都挑了陸董寫的物理一本通，三民出版的，特色是內容詳細，把高二會考的部分融於高一物理之中，同時若想要向北模宣戰這本書也提供了不少北模題目可以大略試試身手，解答也從不拖泥帶水，而且時近代物理和現代物理的應用甚至把高三下的東西補充了一點進來以利理解（其實九五課綱的基礎物理的基本特色就是，只跟你說有這件事，不告訴你為什麼，例如光的繞射）。不過我當時已經事先在書店選購了晟景出版的應試對策，是台南女中的周青彬老師寫的，其實我選上這本書的理由很簡單，這本書在它的競爭對手中是最薄最便宜的&amp;hellip;&amp;hellip;。的確我當時的策略就是不想砸太多心力在物理這科上面，甚至我的心態是將來就乾脆不碰物理了，因為我認為我沒有念物理的天分，但真相是我只是沒找到適合自己的物理讀書方法罷了。附帶一提，九九課綱上面這兩本書目前都還沒有更新，所以學弟妹不好意思你們可能沒辦法用這兩本書了，實在可惜。&lt;/p&gt;
&lt;h2 id="我補的是鍾怡物理"&gt;我補的是鍾怡物理&lt;/h2&gt;
&lt;p&gt;鍾怡物理？走遍了北車補習街，如何也找不到這個招牌。我就不繼續買關子了，所謂鍾怡物理，其實就是陸董的物理課啦！陸董是一個上課風格獨特的老師，而且不只是獨特而已。先概述一下這位老師的幾個特色好了：第一，上課使用自己編的講義，用自己的一套系統進行教學；第二，走到教室的第一件事情就是把黑板擦乾淨、擦到發亮為止（這形容真的不誇張），並且把粉筆擺好，因為你準備要聆聽一場 well-designed 的課程了；第三，上課非常重視板書的整齊度，每次畫圖都會讓我們休息幾分鐘，但是每次畫圖都非常令人驚艷，就像印出來的一樣，而在寫計算過程時也是非常整齊，同時也是這種教學風格讓我不小心領略到計算空間的重要性；第四、如果恰逢第四節課上課會鼓勵我們買東西吃；第五、上課禁止我們看其他書（尤其是補習班講義），原因很簡單，老師認為我們有辦法在他的課中獲益比自己看書還多，而這也是事實；第六、第一堂課會和我們約定好幾個原則，若是違反原則他會馬上翻臉，可是其他情況下是個亦師亦友的好老師，而且說話很直很中肯。當然還有太多其他的特色，在此不一一贅述，若是能親身體驗才更能明白。此外，我覺得陸董上課還有一大特色，就是「自信」。&lt;/p&gt;
&lt;p&gt;老師有自信，學生才能對於他們的學習有自信，這也是我在社團內對於課程講師的一個大原則。我想我領悟到的其中一個讀書方法就是自信的重要性，碰到題目時，若是猶豫不決，題目永遠不可能解開，但若是能抱著自信去嘗試，只要觀念是正確的，基本上沒有什麼題目是行不通的，只是不同的方法會經歷不同複雜度的計算過程，但是條條大路都能通往羅馬，就別太在意了，只是準備好足夠的計算空間先吧。我覺得能在高三遇到陸董這樣的好老師應該是我三生有幸吧，信不信由你，物理從我的最弱科變成我的最強科，指考前一個月我連北模試題都可以寫到 90 分以上，同時我甚至還考慮過大學要不要去雙主修個物理呢，當然這是以後的事情了。在此我也在這邊感謝陸老師一年的教導。&lt;/p&gt;
&lt;h2 id="致命的弱點"&gt;致命的弱點&lt;/h2&gt;
&lt;p&gt;一直以來，我在學科的弱點就是粗心。有時手比腦袋還快，也有時輸入跟輸出不一致，各式各樣粗心的原因層出不窮。其實粗心反過來說，就是不夠小心，解決方法之一當然就是：小心一點，只是誰又能在時間緊湊的考場上保持該有的冷靜呢？&lt;br&gt;
所以我採用的方式是：拿一本筆記本，分章節分範圍一條一條寫下自己曾經不小心而犯錯的地方，並且每次再犯錯就在旁邊加上一筆正字記號。這個方法一開始是我在對付學測數學時想到的，物理我是一直到4/18確定要考指考後才啟用這個方法，而此時我的物理實力其實也已經有一定的基礎了。我覺得這個方式還蠻有用的，其實只要寫下來大部分的錯誤都不會再犯太多次，而且如果記的時間夠長的話，這本筆記本還會變成考前衝刺的利器！&lt;/p&gt;
&lt;h2 id="指考物理衝刺"&gt;指考物理衝刺&lt;/h2&gt;
&lt;p&gt;我挑選的是陸董和大部分老師都很推薦的大滿貫物理講義，是南二中的陳世清老師編寫的。這本講義的最大特色是，重點有抓到、分量剛剛好（不像綠皮書厚的很誇張）。兩百多頁也許有人會認為多，但實際上我大約三十天就寫完了（也是因為我比較晚開始讀指考所以情非得已一定要用飆的）。這本書寫完之後其實我就直接開始挑戰歷屆&amp;hellip;&amp;hellip;.北模試題！當初會做出這個決定的原因是我想要練自己在一群混雜各章題目的試題中順利地見神殺神、見鬼捉鬼的能力。剛開始的確會有點挫折，但越挫就會越勇，後來寫歷屆指考就變成小 case 了。雖然常常聽說北模試題出的不好，但只是在於＂難度＂方面調控的不好，實際上那些題目還是值得拿來練習的，不過還是一句話，抓住解題的觀念，而不是記憶每個題目的特別解法。&lt;/p&gt;
&lt;p&gt;至於成效怎樣呢&amp;hellip;&amp;hellip;至少我很滿意我現在這個分數，雖然沒有達到當初立下要考滿分的目標，但也夠了。&lt;/p&gt;
&lt;h2 id="後記"&gt;後記&lt;/h2&gt;
&lt;p&gt;從這麼長一篇文章裡面要撈點有用的經驗感覺會像是大海撈針呢&amp;hellip;&amp;hellip;，但各位看倌就辛苦點吧，至少這樣才能保持住我這篇心得的原汁原味。有時候我在算物理時會想，高中物理是不是太過理想化了，但後來仔細思考，這其實只是方便我們學習吧，當我們越學越深入，也許要考慮的東西就會越來越多，像是阻力、能量散失等等，所以其實距離物理在現實中的應用還有一段距離吧，只是不學好基礎，何談應用？也許這就是高中教育的目的了。第二篇也打完了，我也不預告下一篇會是什麼，因為我也不清楚我會不會打下一篇，你們以為我很熱心一直想分享自己的經驗給需要的人嗎？其實我主要只是想記下這些東西而已，當作未來的一種懷念吧。&lt;/p&gt;
&lt;p&gt;by YDW 2012.07.11&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>[高中科目心得] 自修一年生物的小心得</title><link>https://dwye.dev/post/20120710_061600/</link><pubDate>Tue, 10 Jul 2012 06:16:00 +0800</pubDate><guid>https://dwye.dev/post/20120710_061600/</guid><description>
&lt;h2 id="緣起"&gt;緣起&lt;/h2&gt;
&lt;p&gt;先說說緣起好了。從高一開始我就一直待在二類班沒有轉班，當時的心態是我不想當醫生，所以也就直接把三類排除在外了（那時我不知道三類還有那麼多選擇&amp;hellip;&amp;hellip;也是一大疏失吧），當時很天真的想說把探索志願這件事情留到以後再說，先在二類待著，直到我的腳踝中了一箭&amp;hellip;&amp;hellip;沒錯一直到升高三的暑假我才發現自己有興趣的是生物化學領域，所以就毅然決然的抱起自修，開始一趟漫長（但不怎麼艱辛）的生物自讀旅程&amp;hellip;&amp;hellip;。&lt;/p&gt;
&lt;h2 id="從喜歡上生物開始"&gt;從喜歡上生物開始&lt;/h2&gt;
&lt;p&gt;剛開始的志願其實也沒有很確定，會開始自讀生物只是在因緣際會之下在垃圾堆（註：學長姐每年不要的書都會丟在一起，任我們自取）翻到了一本高二上生物自修（大約 95% 空白），又剛好因應學測需求，就開工了。卻那知我在第一個禮拜就把高二上解決了，主要就是劃重點、寫習題檢討這樣。那時感到有點欲罷不能（而且學測生物會考到高二下），所以就上網查了一下哪本生物自修比較有名，而我查到的就是：翰林出版南一中郭人仲老師所編寫的自修！除了觀念條理清楚外，課外補充也不少，詳解也不馬虎很精準，從神經和免疫一開始我又一頭栽進了生物的世界！&lt;/p&gt;
&lt;p&gt;你以為我到這就已經喜歡上生物了嗎？還太早！真正的功勞我必須首推暑假時許偉傑老師所上的生物課！他的教法有一個鮮明的特色，就是很有自己的一套系統。我們上課的人甚至只需顧著看黑板抄筆記就好，講義也不用瞄任何一眼就能順利地跟著老師的腳步走，再加上時時穿插的個人經驗分享（有種講法叫「廢話」，但我不覺得那些話有資格被稱為廢話，至少我們都聽得津津有味），以及我回家的每天思索，其實生物還是蠻有趣的一個科目嘛！&lt;/p&gt;
&lt;h2 id="跟著進度唸"&gt;跟著進度唸！&lt;/h2&gt;
&lt;p&gt;就在許偉傑老師的生物課之前，我就已經用飛快的速度把高二生物唸完了，當時我幾乎就是車上唸生物，上課聽生物，回家繼續唸生物&amp;hellip;&amp;hellip;。由於之前對於翰林版那本自修印象不錯，因此我在高三進度的部分也就繼續使用翰林版的生物自修。我記得暑假時和那年的校內三類組榜首閒聊時，我告訴他我決定高三要自修生物，他告訴我高三生物和高一高二的都不一樣，難度增加許多，然而他最後還是鼓勵我，在此也順便感謝學長當時的鼓勵。從第一章緒論，就讓人感受到高三生物的不一樣：我即將進入真正生物學的範疇，要開始以科學專業的角度看一些事情了，而不是只做通識性的認識而已。&lt;/p&gt;
&lt;p&gt;其實我認為就這種按部就班的唸法，對我來說到是蠻順利的，又剛好在此我也領悟到了一些其他科的學習方法，像是數學跟物理科拿到第一次段考 90 分等，當時有種滿面春風的感覺。除了進度之外，我也替自己安排大約每唸兩章就複習一次（把之前看過的重點和筆記再看一遍），然後每次段考前要讀完那次段考範圍等。&lt;/p&gt;
&lt;h2 id="當學測過去之後"&gt;當學測過去之後&lt;/h2&gt;
&lt;p&gt;我也不知道為什麼我還是每一科都照著進度讀！也許是我一開始就隱隱約約感覺到自己指考的可能性吧。這學期我多買了一本翰林版的期考試題挖挖哇（其實也不是這本書多好啦，只是剛好符合我想寫段考題的需求）。當時我聽一些三類的朋友說，正常生物的段考分數都是 7X 分，也就是 1/4 的正確率嘛，而的確在寫得當中會有一些題目還蠻刁鑽的（但比數學好多了&amp;hellip;&amp;hellip;），但是太機車的題目或是有些甚至超出範圍的題目我也沒有理它就是了，到最後發現其實我的答對率不錯嘛。第一次北模我有拿考卷來寫&amp;hellip;&amp;hellip;成績也算可以，就只輸我們班另一個三類的 3 分而已（那個人後來上了名古屋大學，超厲害！）。&lt;/p&gt;
&lt;h2 id="指考戰士的生活"&gt;指考戰士的生活&lt;/h2&gt;
&lt;p&gt;其實 4/18 放榜後一開始我的重心是以救物理和背完 7000 單為主，也因此有一小段時間沒有碰生物，但也是一個禮拜多幾天而已。對我來說，生物是一科耗費腦力很少的科目（尤其是複習的時候），有點像是另一個社會科吧，所以我把它排在一天的最後，也就是在晚上讀。由於我認為自修已經提供了足夠資訊，因此我在選購總複習參考書時就盡量以簡單扼要的類型為主，不同於大部分人選的華逵精通總複習，我選擇了言日中所編寫的新高中生物超速度記憶，而這邊的言日中，其實就是前幾段提到的許偉傑老師啦！而歷屆試題的部份我選擇了捷學國際出版的，一開始是看在它解說詳細的份上選擇的，但後來寫一些發現是一個失策，因為其中有不少錯誤（當然也有可能是因為資料多，錯誤發生的次數也跟著上升），而且購買這個出版社其他科歷屆試題的同學也跟我說，有些地方的解法或答案是錯的。&lt;/p&gt;
&lt;h2 id="考前的衝刺"&gt;考前的衝刺&lt;/h2&gt;
&lt;p&gt;等到考前大約二十來天的時候，我把上面那本總複習參考書讀完了，其實那本書要讀完並不用花很久的時間，只是有些較深入的觀念真的要自己補充（當然指考不一定會考那麼難）。因此我又在某次逛書店的時候買了一本複習的題本，是補教老師姜孟希寫的題庫，從這麼多本書中選擇它的理由是因為題型夠活，而解答也很精闢。然而有些題目真的是有一點刁鑽，我的處理方式也是像之前寫段考題那樣。這本題本有點厚所以我就沒有寫完了，只有在每次有時間的時候挑選我覺得重要的章節來寫（動物生理學、內分泌學和胚胎學、植物生理學、新陳代謝及能量轉換、人體的防禦系統、組織學、植物構造與運輸&amp;hellip;&amp;hellip;好啦其實我還寫蠻多的），而大部份我都是利用通勤的時間在寫。而在最後一天，我就拿起那本總複習，快速掃過之前畫的重點，準備赴戰。&lt;/p&gt;
&lt;h2 id="總結"&gt;總結&lt;/h2&gt;
&lt;p&gt;其實到後面感覺一直都很穩，寫完歷屆就知道大概會考什麼樣的東西，也懂得去避開一些太細節的記憶，找到每章節的重點。此外，在讀的過程中，常常感受到生命實在不愧是累積億年的奇蹟，居然能協調的如此奇妙！但我將來要克服的東西其實還很多，像是之前一直都逃避沒去接觸的解剖。雖然我不確定我的讀書方式是否是最好的，但至少這次的指考生物成績還算是滿意了，因此作此心得文，當作記錄之外，也給需要的人作為一點參考。&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>進台大重要嗎</title><link>https://dwye.dev/post/20120219-to-ntu/</link><pubDate>Sun, 19 Feb 2012 18:38:00 +0800</pubDate><guid>https://dwye.dev/post/20120219-to-ntu/</guid><description>
&lt;p&gt;　　台大，既然能擁有著台灣第一學府的名號，也必定有它的本事。台大有著自由開放的學風、豐富的學習資源、許多厲害的教授、強大的同伴兼競爭對手&amp;hellip;&amp;hellip;.等等。但凡是有正面的誇獎，就會有負面批評：在企業界中最受歡迎的學生是成大畢業的，而不是台大，為什麼呢？有人說，台大學生太過高傲，仗勢著自己的高學歷，就要企業給他們更好的待遇，相較於成大學生而言，成大校友形成了一個堅強的體系，校內教授也積極努力的給學生學習資源，因此成大出來的學生雖然比較被動，卻也比較易於「駕馭」，有著一種願意從基層打拼的感覺。也難免成大的學生在企業界占盡優勢。&lt;br&gt;
　　但其實這只是學校的走向問題：成大很明顯是一所為企業界培育人才的學校，清大交大也有這種走向。然而台大卻是往台灣的學術中心發展，台大培育出的學生不一定是為企業界量身打造的，不過不能否認的是他們在學術領域的表現，尤其是在醫學領域台大明顯是最優秀的。台大擔當著讓台灣減少對國外的依賴，讓台灣在學術領域走出自己的一片天的重要角色。說實在的，台大培養的主要不是企業員工，而是研究人才。&lt;br&gt;
　　那麼就回到根本的問題，進台大重要嗎？&lt;br&gt;
　　身為一個北部人，北部的第一志願非台大莫屬，很少人願意到南部念成大，難免是因為已經習慣在北部的生活，要離鄉背井讀書不容易，這現象又以台北最為嚴重。台北，中華民國現在的首都，也是全台灣工商最發達的國際城市，上層階級的有錢人大多聚集在此，也造成了台灣南北發展不均的現象。這些社會中較上層的人多半擁有較好的資源，能給小孩教好的教學，再加上能夠待在上層階級的他們，家族的資質也不會太差，因此就形成了一種「壟斷」的現象：有錢人的孩子較聰明，聰明的他們又能再度成為有錢人。既然台北市聚集了這麼多聰明人，可想而知北部的第一志願台大的分數必然會被衝高。&lt;br&gt;
　　但如果台大的分數只是「被衝高」的，進台大似乎就不顯的那麼重要了，只要願意離家讀書，在成大、清大、交大四年學成後翻身的可能性也不會遜於台大學生。但現實殘酷地，台大就是擁有著最多的資源，雖然自由開放的學風使台大不會太強迫於學生必須一步步走在教授安排的軌道上，不過相對而言，有心學習且有志向的學生在這所大學就更能取得優勢。&lt;br&gt;
　　不如問問自己，我想要的是什麼？&lt;br&gt;
　　台大在綜合評比下一定是最優秀的，不過在個別領域上前幾名的大學卻各有勝負，例如清大理學院、交大資工、陽明生科等等也都是數一數二的，但它們在台大的對手都掛著台大的光環，所以這些科系在台大的分數就隨著潮流一直被衝高。只是我們不能只因為前幾名的順序就評斷哪個好、哪個壞。講句實在話，這些大學、這些科系都算很好了，若不是追求頂尖，又何必斤斤計較？更何況這些不是台大的學校也常培育出不輸台大的學生，說不定你我就會是那個人，那刁鑽於這細細的錄取分數上就顯得更沒有意義了。&lt;br&gt;
　　套一句陸董說的話，大學不是人生的「終點站」，只是個「中點站」，重要的是我們在社會上的表現，人一生的成敗總是在入棺之後才能評斷的，就像希特勒在發動第二次世界大戰之前，也曾經是當時世界上令人崇拜的領導人，也有像林書豪那樣念了全球頂尖的哈佛大學卻出來打籃球在球壇上嶄露身手的人。擺在我們面前的是許多條路，條條大道都能抵達羅馬，只要我們能清楚地知道方向並抱著決心走下去。&lt;br&gt;
　　最後再問一次自己，進台大重要嗎？&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item><item><title>VB.NET 之物件陣列</title><link>https://dwye.dev/post/vb-net-object-array/</link><pubDate>Mon, 06 Jun 2011 18:41:25 +0800</pubDate><guid>https://dwye.dev/post/vb-net-object-array/</guid><description>
&lt;p&gt;如果有人同時用過 VB6 跟 VB2008（或更新版的 VB），必然會發現其中有許多的差別，在此不一一列舉，不過我想當有人要寫一個踩地雷的程式時，沒有了物件陣列或多或少會帶來一些的不方便。因此筆者在研究了一段時間之後，找出了 VB.NET 中物件陣列的使用方法：它必須要直接使用 &lt;code&gt;Dim&lt;/code&gt; 宣告！&lt;/p&gt;
&lt;p&gt;以下皆以 VB2008 做為示範。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-vb" data-lang="vb"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;Dim&lt;/span&gt; btn(9,9) &lt;span style="color:#f92672"&gt;As&lt;/span&gt; Button
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如此的直觀。&lt;/p&gt;
&lt;p&gt;不過在程式碼中打上了這一句之後，點下開始偵錯，卻會發現什麼都看不到，原因為何？因為現在我們只是把它們「宣告」出來，但我們還要把它們「建立」成一個真正的物件，並且「擺上」表單。&lt;/p&gt;
&lt;p&gt;選擇能在一打開程式就會發生的 &lt;code&gt;Form1_Load&lt;/code&gt;，利用巢狀迴圈一個一個跑。&lt;/p&gt;
&lt;p&gt;有人可能會問，為什麼不在一開始宣告物件陣列時就把「&lt;code&gt;New&lt;/code&gt;」加進去呢？因為若如此做，VB2008 便會跑出一條可愛的藍色蚯蚓，告訴你「不可以使用 &lt;code&gt;New&lt;/code&gt; 宣告陣列」。&lt;/p&gt;
&lt;p&gt;接下來再把按鈕「擺上」表單吧，一樣要擺在巢狀迴圈裡面。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-vb" data-lang="vb"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;Me&lt;/span&gt;.Controls.Add(btn(i, j))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;記得把這行放在「&lt;code&gt;btn(i, j) = New Button&lt;/code&gt;」下面，不然因為程式碼是由上往下跑的，當程式要「擺上」按鈕的時候會發現這個按鈕並沒有被「建立」出來，結果就等於──沒有「擺上」。&lt;/p&gt;
&lt;p&gt;這時若有人禁不起誘惑點了開始偵錯，便會發現一個殘酷的事實：所有的按鈕都疊在一起，都長的一模一樣，好像只有一個按鈕似的，而且啊，還呈現一種很噁心的長條形狀！聰明如我們，腦中應該立刻浮現一件事：我們要去調整物件屬性，在檢視工具設計那邊找了半天，剛剛宣告的按鈕一個都找不到，要調屬性？別鬧了！既然我們是用程式碼把它們宣告出來的，調整屬性也要從程式碼去調整！不知道諸位是否還記得在 VB 社課中講師一直強調的「&lt;code&gt;物件名稱.物件屬性=屬性值&lt;/code&gt;」？&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-vb" data-lang="vb"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;btn(i, j).Size &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;New&lt;/span&gt; Size(30, 30)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;記得要打在「&lt;code&gt;btn(i, j) = New Button&lt;/code&gt;」這行後面，因為我們要先把物件「建立」起來它們才能擁有屬性，其他屬性的調整以此類推&amp;hellip;&amp;hellip;我相信等到大家打完全部的屬性，手都打到斷掉了。有個陳述式叫「&lt;code&gt;With&lt;/code&gt;」，是專門用來調整一大串物件屬性的：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-vb" data-lang="vb"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;With&lt;/span&gt; btn(i, j)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .Left &lt;span style="color:#f92672"&gt;=&lt;/span&gt; i &lt;span style="color:#f92672"&gt;*&lt;/span&gt; 30 &lt;span style="color:#f92672"&gt;+&lt;/span&gt; 20
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .Top &lt;span style="color:#f92672"&gt;=&lt;/span&gt; j &lt;span style="color:#f92672"&gt;*&lt;/span&gt; 30 &lt;span style="color:#f92672"&gt;+&lt;/span&gt; 20
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .Size &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;New&lt;/span&gt; Size(30, 30)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;End&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;With&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;使用方法不言而喻。&lt;/p&gt;
&lt;p&gt;再來進行偵錯，就會發現你的按鈕一個一個立正站好排在那兒等你了。&lt;/p&gt;
&lt;p&gt;談完了物件跟屬性，事件呢？我們得幫這些按鈕新增一些事件讓它們有些功用，可是不免又會碰到一些麻煩：如何去新增？上面的類別名稱根本找不到我們剛鋼宣告的「btn」啊！程式碼萬能，沒有程式碼萬萬不能，萬能的程式碼又派上用場了！&lt;/p&gt;
&lt;p&gt;首先，再宣告一個按鈕：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-vb" data-lang="vb"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;WithEvents&lt;/span&gt; btnEvent &lt;span style="color:#f92672"&gt;As&lt;/span&gt; Button
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;使用「&lt;code&gt;WithEvents&lt;/code&gt;」宣告，這樣宣告出來的按鈕就會直接被配置有原本按鈕該有的事件，跟「&lt;code&gt;New&lt;/code&gt;」一樣，它是不能拿來宣告陣列的。如此做之後，我們就可以在類別名稱那裡找到「&lt;code&gt;btnEvent&lt;/code&gt;」這個按鈕，也就可以建立一個 &lt;code&gt;btnEvent.MouseClick&lt;/code&gt; 事件了。&lt;/p&gt;
&lt;p&gt;接下來我們就只差一個工作，這是一個筆者認為非常厲害的步驟，就是把這個沒有事件的按鈕陣列在按下按鈕時「導向」&lt;code&gt;btnEvent.MouseClick&lt;/code&gt; 這個事件&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-vb" data-lang="vb"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;AddHandler&lt;/span&gt; btn(i, j).MouseClick, &lt;span style="color:#f92672"&gt;AddressOf&lt;/span&gt; btnEvent_MouseClick
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;大致上我們可以將這行解釋為「&lt;code&gt;AddHandler&lt;/code&gt; 被引導之事件, &lt;code&gt;AddressOf&lt;/code&gt; 目的地」。&lt;/p&gt;
&lt;p&gt;這樣，當我們一按下物件陣列 &lt;code&gt;btn&lt;/code&gt; 中的任一按鈕，就能引發 &lt;code&gt;btnEvent.MouseClick&lt;/code&gt; 這個事件中的程式碼了。&lt;/p&gt;
&lt;p&gt;剩下就回到大家熟悉的部分了，如何把這東西變成一個踩地雷程式就靠你們自己囉，在此祝大家和 VB.NET 相處愉快吧！&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;（原文刊載於師大附中電算社 32、33 屆社刊）&lt;/p&gt;
&lt;p&gt;更多：&lt;a href="https://github.com/dwy6626/VB-Handouts"&gt;https://github.com/dwy6626/VB-Handouts&lt;/a&gt;&lt;/p&gt;</description><copyright>本文原創部分採用 創用 CC 姓名標示-非商業性-禁止改作 4.0 國際授權條款 授權。</copyright></item></channel></rss>