Posts

正音班 - 台大語文中心

最近上了台大語文中心的正音班,可以參考開課列表,不定期會推出不同課程。

兩週四堂課,每周三、五 18:30~19:15,費用共 $3,300,上課教授為 Tom Sellari,是在台灣待很久的紐約人!

先說結論,滿推薦的,課程時間短,可以讓你有一個快速入門的方向,課程安排有兩大主題:

  1. Minimal pairs and troublesome sounds
  • vowels
  • consonants
  1. Speaking naturally
  • fluency and linking
  • Phrasing and Pauses
  • Emphasis

我自己學習動機是發現 zoom 或是 google meet 的 caption 功能其實不是我講的每一個單字都可以辨識出來,加上跟外國同事開會常常會聽不懂我講的單字是哪個導致很卡,最後一根稻草大概是之前跟同事聊到優勝美地,我以為他發音比較像是 yow·sun·might,結果是 yow·seh·muh·tee,雖然他們是有聽到,但我尬爆。於是就找找看有什麼學習資源可以幫助我更好的跟外國同事聊天。

課程一開始會透過一組又一組的單字來說明大家容易搞混的母音發音,譬如說 [peak, pick], [eat, it],這組要區分的是 long e & short i 的差別,課堂中會藉由分組練習的方式,來互相確認有沒有發正確,老師課堂上也會強調一些口舌之間的動作,譬如說 long e,要發正確其實嘴巴要出一點力,所以嘴巴懶的時候都會退化成 short i。

我自己觀察到比較台式的發音,問題大多是會在過度省略需要的口部動作,子音的部分這邊問題比較少,比較特別的地方大概是區分 SH /ʃ/ and ZH /ʒ/ ,單字都會是 sh,但發音會不一樣,可以參考這個 youtube

教授課堂上也會提一些其他國家的口音特點,以墨西哥來說的話 sh sound 他們通常都會發成 voiced sh sound、加州口音的話很喜歡用 like,跟語句最後都會比較上揚,想到 Valley Girl Accent #1 #2、日式口音的話就是 r,l 不分、台式發音的話我自己觀察大多是語調比較平,然後母音沒有發正確的問題、印度發音的話教授有特別說除了有一些子音不同以外,另外 rhythm 也有很大的不同,導致其實連他都聽不太懂(但我覺得是 native speaker 的謙虛 XD)。

富良野季末滑雪,入住王子飯店

2024/04 趁離開上一份工作,四月安排了去日本玩,這是疫情以來後第一次到日本走跳。 距離上次出國已經四年,還卡在桃機自動通關前面超丟臉,出發前也沒有不知道要辦 Visit Japan, 還在飛機上坐等空服員姊姊發申報相關紙張,結果第一個下飛機,然後卡在櫃檯前面乖乖寫申報,已經變成旅遊白痴。

這次主要目的是滑雪,自從 2019 年去藏王體驗了一下有點念念不忘,四、五年來不能說沒有成長,成長最多的大概是肚子與內臟脂肪, 人也從原本是個 2x 的精神小伙,到現在是個神經的肥宅,大家一定要正視工作造成的健康危害 (?

行前準備 #

四月還是滑得到雪的,先訂了新千歲機票,再來找滑雪場,出乎意料大部分北海道雪場都會標營業到 5/1 號, 最後問一下滑雪大大同事,他推富良野,看了一下新王子飯店 ski in/out 每晚價錢也還滿便宜的,交通看一下沒問題就決定是富良野了。

但滑雪還是要老天賞臉,原本出發前看積雪 145cm,結果抵達當天剩下 110cm,消逝的速度比我逐漸退後的髮線還快。

教學部分找了 Furano Snow School 因為季末的關係選擇也不多,大多學校只會開到三月。 對初學來講我實際上了,兩天各兩小時總費用是 ¥40,000,費用相比其他學校來說已經是偏低的了,但一個人找教練真的也是滿貴的 XD 其它備選的學校包含:

後來領悟了一個道理,如果要費用較低的話盡量找當地的,教學品質穩定但有語言障礙。 如果是多人均分的話應該還是會找台灣教練吧。

交通部分因為四月找不到飛旭川的飛機,要馬 JR 要馬巴士,巴士站的起程站牌我 google 半天看不太懂,最後還是選 JR。 中間要在瀧川轉乘根室本線 體驗有點差,車廂僅兩節?也因為沒有電氣化的關係,有一股柴油味,開的也滿慢的,、從舒服的特急 KAMUI 轉乘這個會有巨大落差,回程就直接搭巴士回札幌了。

到場滑雪 #

四月的時候新王子飯店其實不能 ski in/out,並不是不能直接進出雪道, 而是從旅館出去的那條雪道,纜車沒有營運,有開的只有中間 gondola 跟上面的一條纜車, 所以每天最痛苦的時候是滑完然後再把裝備走上坡扛回飯店櫃子,整體來說我很滿意教學的品質,受限於體力關係兩天只有學玩落葉飄,對大多數台灣人而言這進度應該落在倒數 20%,但正確的落葉飄對我來說是個重要里程碑,代表了即使速度慢,我也可以走綠線慢慢的飄下山了。

抵達當天晚上,可以看到最靠近飯店的雪道積雪已經剩沒多少了 從房間看到的雪道 新王子飯店的早餐 離開那天的雪道模樣,這幾天都充滿晴天娃娃的祝福 離開那天的雪道模樣 雙人床房型 森之時計咖啡屋

Gsutil Need Pip Install Pyopenssl

Need to create some signed-url links, using following command.

gsutil signurl -d 2d service-account-key.json gs://my-gcs-bucket/my-object

But the response keeps auguing

The signurl command requires the pyopenssl library (try pip install pyopenssl or easy_install pyopenssl)

Already check those variables and install pyopenssl thousand times.

CLOUDSDK_PYTHON=path/to/python
CLOUDSDK_PYTHON_SITEPACKAGES=1

After checking the source code under platform/gsutil/gslib/commands/signurl.py

try:
  from OpenSSL.crypto import FILETYPE_PEM
  from OpenSSL.crypto import load_pkcs12
  from OpenSSL.crypto import load_privatekey
  from OpenSSL.crypto import sign
  HAVE_OPENSSL = True
except ImportError:
  HAVE_OPENSSL = False
  ...

The load_pkcs12 has been removed from PyOpenSSL==23.3.0, so I install the previous version and get works.

Why the secrets Module is the Ideal Choice for Generating Random Strings in Python

以前就一直很愛用 ruby 提供的 securerandom 來產生隨機字串,

但之前長時間使用 Python2 進行開發,一直忽略了 Python3 應該也會有類似的 module,

直到昨天有需求又查了一下發現,ㄏㄏ早在 3.6 就有提供這個 module 了,試一下跟 securerandom 87% 像,推薦給大家

簡單來說用 secrets 的好處是比起傳統 random module 產生出來的隨機字串

  • 更具有密碼學上的安全性
  • 更快,更簡單可以產生字串

原理的話其實就是用 os.urandom 取代 random module 內的擬隨機演算法

#### 偷懶版本,要注意 32 是 bytes,實際產生出來的長度因為 base64 encode 的關係會超過 32
from secrets import token_urlsafe
print(token_urlsafe(32))

#### 長一點的版本,使用 secrets.choice
import secrets import string
alphabet = string.ascii_letters + string.digits
random_string = ''.join(secrets.choice(alphabet) for i in range(16))
print(random_string)

ChatGPT 的回答 #

The benefits of using the secrets module to generate random strings (and other values) over the random module are:

Cryptographic security: The secrets module uses a cryptographically secure random number generator provided by the operating system, which is designed to be resistant to prediction and manipulation. The random module, on the other hand, uses a simpler algorithm that is not intended for cryptographic use.

Convenience: The secrets module provides a simple and intuitive interface for generating random values, without requiring manual seeding or other setup.

Efficiency: The secrets module is optimized for generating large amounts of random data quickly, and can generate random strings and other values much faster than the random module.

References #

Orbstack the Docker Desktop Replacement

身為一個資料水管工,在開發的時候常常需要用 docker compose 把整套 Airflow 拉起來測試,

這時候我的 intel-based 筆電常常就會起飛,在咖啡廳都被路人瞪,讓我非常不好意思。

昨天試玩了一下 OrbStack,覺得好棒棒

第一點是 OrbStack 啟動速度超級快!

以前覺得 Docker Desktop 記憶體吃太兇的時候就想讓他重開,要等等上一分鐘,

現在 OrbStack 啟動只要數秒,連偷個懶都不行,很不方便。

再來是資源使用率部分,

Memory 顯著有感,以前大概會吃到 10G swap,換了之後大概只吃到 3G,

CPU 部分也滿不錯,之前開 Docker Desktop 的時候其他東西都會比較卡,

開發體驗很差,索性開發完才拉起來測試,現在可以邊跑邊開發了,法喜充滿。

轉換的痛點

  • 需要升級到 macOS Ventura (13.2?)
  • 所有 image 要重 build

behind the scenes

  • 背後的原理,是利用 macOS Ventura 提供的 Virtualization Framework,share kernel 跟 windows 的 WSL2 很像
  • 未來會收錢,且用且珍惜

memory

BigQuery JSON_VALUE_ARRAY, JSON_QUERY_ARRAY 差異跟小雷

JSON_VALUE_ARRAY 只吃 scalar value

任何不在 {string, number,boolean} 都會變成 NULL, 像是

select JSON_VALUE_ARRAY('{"product_ids": [UCCU]}', "$.product_ids");

--
Row	f0_	
1	null

但如果裡面有 NULL 則會噴錯

select JSON_VALUE_ARRAY('{"product_ids": [null]}', "$.product_ids");

Array cannot have a null element; error in writing field f0_

啊如果我就是要 scalar value 裡面又可能有 null 怎麼辦? 這邊可以改用 JSON_QUERY_ARRAY 替代,但因為 output 不同(多了 double quote),視資料情況可以直接 trim 掉處理

SELECT
    ARRAY(SELECT TRIM(item, '"')
FROM
    UNNEST(JSON_QUERY_ARRAY('{"product_ids": [null, 1, 2, 3]}', "$.product_ids")) AS item);

打包 markdown 筆記並上傳到 R2 (S3 like service)

自從用 markdown 開始寫工作日誌與筆記,一直放在 iCloud 上面,之前看到 iCloud 掉資料的新聞覺得好像還是有必要多異地備份一下,那時候選了 S3,看重的當然是 11 nines 的耐用性,這樣畢生筆記萬無一失了吧。

這陣子發現 Cloudflare 的 R2 可以開始用 beta 版本了,基於我本人是 Cloudflare 無腦粉,馬上就想把目前的筆記也丟一份放在上面,於是就來研究一下。

首先 Cloudflare R2 跟 S3 一樣是 11 nines,對外宣稱的耐用性跟 S3 一樣,再來是每 GB 價錢,R2 目前是 $0.015 per GB per month,跟 S3 Virginia $0.023 比起來低了 35%,再來是資料不收輸出的頻寬費用,這點我自己很好奇,那不就可以拿來做一個免費的圖床?最後是跟 s3 相容的 api 把 api token 申請好之後就可以直接用 aws cli 上傳檔案。

一開始到 https://dash.cloudflare.com/sign-up/r2 申請服務,可以看到開 bucket 是不用選 region 的,下面說明 R2 buckets are automatically distributed across Cloudflare's data centers 另外也注意到 bucket name 應該是帳號內不能相同,跟 AWS 整個 region 不能有相同的 bucket name 不一樣。

抓網站內容做成電子書 (epub)

alt

買了 BOOX note5 之後, 覺得電子紙螢幕呈現效果真舒服, 所有要長時間閱讀的東西都想丟進去. 另外發現雖然系統用 Android 11, 理論上可以裝所有 Android App, 包含瀏覽器, 不過在裝了一堆 App 發現都是虛幻, 排版與文字呈現還是用 epub 格式最好.

本次範例使用 EbookLib, requests

以及 mark_mew 大大的關於我幫新公司建立整套部屬流程那檔事 為範例 感謝 mark_mew 大大分享自身經驗

另外粗粗產生出來的 epub 還是有很多排版問題要修, 像是圖片不見了, script tag 跑出來了, 在過一層 strip_tags 應該會好一點. 看來最適合的還是轉小說進去 (?

這次網頁數量不多用 requests 抓抓就好, 就不寫 scrapy 了, 抓別人網站注意禮貌

程式分成四段, 第四段跟 ebooklib 範例程式只有差異在產生 sections 部分

  • 抓內容網址
  • 抓內容 -> local file
  • 建立章節
  • 寫成 epub
from ebooklib import epub
from lxml.html import fromstring
from tqdm import tqdm
import os
import requests

# 抓內容的網址
urls = []
headers={
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36"
}
page_urls = [
    'https://ithelp.ithome.com.tw/users/20141518/ironman/4653',
    'https://ithelp.ithome.com.tw/users/20141518/ironman/4653?page=2',
    'https://ithelp.ithome.com.tw/users/20141518/ironman/4653?page=3',
]
for url in tqdm(page_urls):
    response = requests.get(url, headers=headers)
    tree = fromstring(response.text)
    urls += [s.strip() for s in tree.xpath('//h3[@class="qa-list__title"]/a/@href')]

# 抓內容
for url in tqdm(urls):
    fname = url.split('/')[-1]
    with open(fname, 'w') as wf:
        response = requests.get(url, headers=headers)
        wf.write(response.text)

# 建立章節
def build_sections():
    sections = []
    for fname in tqdm(sorted(os.listdir('./ebooks'))):  # 利用檔案名稱排章節順序
        if '.' in fname:
            continue
        with open('./ebooks/' + fname) as f:
            tree = fromstring(f.read())
            title = tree.xpath('//h2[@class="qa-header__title ir-article__title"]/text()')[0].strip()
            content = ''.join(list(tree.xpath('//div[@class="qa-panel__content"]')[0].itertext()))
            content = content.replace('\n', '<br/>')
            section = epub.EpubHtml(title=title, file_name=fname, lang='zh-hant')
            section.content = content
            sections.append(section)
    return build_sections

# 寫成 epub
from ebooklib import epub

book = epub.EpubBook()

# set metadata
book.set_identifier('ithome_ironman_4653')
book.set_title('關於我幫新公司建立整套部屬流程那檔事')
book.set_language('zh-hant')
book.add_author('mark_mew')


# create chapter
sections = build_sections()

# add chapter
for section in sections:
    book.add_item(section)

# define Table Of Contents
book.toc = [
    epub.Link(section.file_name, section.title, section.title)
    for section in sections
]

# add default NCX and Nav file
book.add_item(epub.EpubNcx())
book.add_item(epub.EpubNav())

# define CSS style
style = 'BODY {color: white;}'
nav_css = epub.EpubItem(uid="style_nav", file_name="style/nav.css", media_type="text/css", content=style)

# add CSS file
book.add_item(nav_css)

# basic spine
book.spine = ['nav'] + sections

# write to the file
epub.write_epub('關於我幫新公司建立整套部屬流程那檔事.epub', book, {})

重寫所有 git author name & email

在家工作之後,幾乎都是用公司筆電,一些個人專案不小心套到公司 email 設定, 想說可能要寫個 script 來處理,沒想到一行就解決了。

git config --local user.name "Kaneshiro Takeshi"
git config --local user.email "your_email@example.com"
git rebase --root --exec 'git commit --amend --no-edit --reset-author'

初老工程師的焦慮

往雲裡去

2022 年滿 32 歲,對目前職涯感到有點迷惘,生活中有很多焦慮,寫下來或許會好一點。

學不動的焦慮 #

4/1 在新公司待滿一個月了,換工作這段期間總是有很多對未來的想法。

今年也看到很多厲害同事都在往理想的目標邁進:

  • 有出國工作的
  • 換到 FAANG
  • 生活重心在育兒上面或即將轉到育兒上面
  • 辭職轉專職顧問的
  • 開書店的

在祝賀與羨慕的同時,心裡不免有些焦慮,我到底想要什麼。

這幾年打算走 individual contributor 這條路,我覺得這條路比較適合我, 尤其在前一間公司看到很多年資比較大的工程師,在發揮影響力的同時,也把家庭顧的很好。

但在現在公司看到都是一群年輕,技術能力扎實,溝通能力強,人又謙虛的同事, 心裡也是有點焦慮。我的競爭優勢在哪?是不是要開始找養老公司了?是不是還是要轉管理職?

視野實現一半的焦慮 #

環境跟人很重要,多與人交往,這兩年的變動都跟同事提供的視野有關。

在 SNS 看到人家說「很多人在 25 ~ 30 歲想找自我,找到最後才發現自己喜歡錢」 我稍微不幸一點,31 歲才發現自己喜歡錢,剛好這幾年大環境變動,薪資水準提升很快, 雖然不比半導體業,至少也是有領到人家地板價。

同事給我的視野還有出國唸書,以軟體工程來講,我覺得出國是最好的方向, 拿到灣區平均 offer,兩三年就可以回本,同時也在科技前沿工作,進可攻退可守。

健康不行了 #

健檢紅字越來越多,眼睛健康越來越差,已經有老花症狀,飛蚊症也變嚴重。 主因我想還是每天上班看螢幕八小時,下班娛樂主要還是看電腦,經歷每天 12 小時的摧殘。 或許也應該思考副業或轉職可能性,三十歲後健康每況愈下,老得很快,希望下一次大衰退不要來得太早。

買房、買車 #

買車就是負資產,但還是好方便喔 XDD 焦慮自己前幾年沒存太多,頭期還是出不起,但也只能慢慢累積。

已經克服的焦慮 #

文章最後總要留點正向的東西

Imposter syndrome

前幾次換工作真的焦慮到爆,為什麼達不到主管的期待?後來漸漸了解:

  1. 每個人經歷不盡相同,過去的經驗不一定能快速換來對公司的貢獻
  2. 說者無心聽者有意。有些事不需要想太多,大部分的情況只要態度正確,就沒有大問題(在對的時間做對的事情)
  3. 氣場不合,他就是不喜歡你。做該做的事情,盡可能正直,他人的主觀想法不容易扭轉,做好該做的事情。
  4. 主管標準比較高。依照自己的節奏做事,不可能一直衝刺的,跟主管討論這方面的期待吧。

人際交往的焦慮

  1. 以前做了很多刻意的表演,才發現人與人互動真誠、正直、自然這幾點都很重要,刻意裝是沒有用的。