來玩玩看 Codewars 吧

為什麼不跟著大家做 Leetcode 反而要玩 Codewars?

Johnny Fang
11 min readJan 8, 2023

最後更新時間:2023–01–08

coding coding coding,可惜 Unsplash 沒找到佛跳牆的圖,什麼佛跳牆?往下看就知道XD | Photo by Mohammad Rahmani on Unsplash

緣由

怕有人沒聽過先說一下,Codewars 是一個可以練習寫程式的網站。

這是我大概一個月前自己去找的,當時因為正課進度超前,很多事情提早做完,有點讓自己太放鬆了,於是想了想,應該會有很多網站可以練習寫程式吧?去找果然就有,於是就選個看起來順眼、有人在用、也有人推薦的來玩玩看,後來就養成這個習慣,只要有空檔就練一下,而本週積分剛超過 100 分,順便來分享 + 推廣。

什麼是 Codewars?

先附上連結,可以點進去摸一下。光是網頁可以選擇深色模式(Dark Mode)就讓我好感度上升,常盯著螢幕尤其看一堆 code,每次點開那種背景色是白色的網站,一點開那一瞬間都覺得快瞎了,所以現在看到可以自由切換顏色模式的都很開心,Replit 更猛,最近還推出色盲模式與其他顏色模式,因此我也很喜歡用 Replit(不是因為我色盲XD,而是看到一間公司在乎使用者甚至考慮到少數不方便情境,真的要大推),目前主要把它當作測試 code 的地方,例如不確定某個語法寫出來會造成什麼效果,我就會跑去 Replit 測。以防你不知道 Replit,它就是一個線上開發環境,可以讓你寫 code、馬上執行看結果之類,請點進連結看看,另外還有一個叫 CodePen,跟 Replit 差不多,只是比較屬於前端,可以搭配 HTML 與 CSS 即時渲染畫面看結果,一開始我覺得好方便但後來不是很喜歡用,當 code 越來越多,CodePen 版面配置很難閱讀大量的 code,因為它把視窗分割可是卻沒有縮小功能(可能我沒發現?),另外,整個畫面其實也還有很多地方可簡化節省空間,後來用 VS Code 比較舒服,其實 Codewars 在解題畫面也有這個問題,版面不夠精簡,且無法自由調整各部分視窗大小,只能選擇放大或縮小某區塊,不過目前這個問題還不算太擾人,因為 Codewars 解題不會太久(就符合我目前程度的題目而言),不需要長時間盯著版面,希望未來可以優化。

主要功能使用上滿直覺的,反正就是練 coding,可以選擇你要練哪個語言,然後有不同模式,像我是選「Fundamentals」,意思是一直練習基本題目,白話就是新手練功區,如果不想一直在新手村可以選「Rank up」,題目會越做越難,還有其他模式就不贅述。既然是練功,有等級制度也不意外,計分方式雖然不太直覺但個人認為合理,有三個東西先搞懂即可,分別是 Rank、Honor、Score:

  • Rank:等級,就是你現在幾等,由 Score 多少而定
  • Honor:最直接可以看到的數字,但不是單純解題拿分數升等的數字,除了解題外,創造題目、參與社群活動例如投票之類,都可以增加 Honor 的數字
  • Score:跟升等有關,但詭異的是在畫面中直接看會看不到,要用它提供的 API 才看得到,但是很簡單,只要把 API 貼到網址列再把最後面改成你的使用者名稱就可以串到了,裡面會有 Score 數字,而這個數字就關乎 Rank,那 Score 跟 Honor 差在哪?簡單說解題可以增加 Honor 與 Score,但 Rank 是由 Score 決定,而 Honor 分數來源則不只解題一種

其實還有其他細部遊戲規則,但個人認為沒有非常重要,反正我就是來練 coding 的,只要能讓人越來越強,他們要怎麼制定遊戲規則我倒是不太在意,很多細部資訊都可以在這裡查詢到

那 Codewars 跟 Leetcode 有什麼不一樣?

都是解題練 coding,相異之處可列舉很多,但最主要差別在於,Leetcode 題目收集自真實公司考題,而 Codewars 題目由使用者自行設計上傳,而 Leetcode 即使是 easy 等級的題目,初學者也不一定看得懂,如果是 Fizz Buzz 這種可能還好,但我隨便找了一題就踩到坑,像 Middle of the Linked List,我以為是陣列,寫完後系統一直說 slice() 不是個 function,但去 Replit 測就正常,結果查了一下才知道原來 Linked List 不是陣列,看了解答發現看不懂因為根本還沒學到這種資料結構,像這類情境就滿浪費時間,也不是現在該做的事,如果這種以後學到會覺得很簡單的東西何必現在去碰然後挖了與現在學習無關的坑導致時間反而花得更多,另外,medium 等級的題數在撰寫這篇文時大約佔總題數一半,這代表如果要刷題基本上刷 medium 效益最高因為很可能面試會碰到,像之前這篇提到的 Integer to Roman 在 Leetcode 是 medium,這題是 AC 2–1 期末技術驗收之一,當初我前後搞了 1–2 個小時,初學者若為了解題每天搞這麼長時間,肯定會壓縮到其他基礎知識學習時間,相較之下 Codewars 基礎題目設定都很單純,所以我選擇先做 Codewars 比較實在,至於為何不要先做 Leetcode 下面還有說明

題外話,在 Codewars 常看到日本元素,例如程式題目叫 Kata,在日文是武術套路的意思,另外還有 Kyu、Dojo等,查了一下母公司是美國的,創辦人也是美國人,不知道開發團隊是不是有日本人還是很喜歡日本文化XD

為什麼不先做 Leetcode?

看過一篇文章,我的想法跟該文作者幾乎一致,在網路隨便查都知道一堆人在刷題,可是對我來說,打好基礎很重要,基礎若不紮實則刷題效益不高,先學會走再練習跑,合理。然而,我發現似乎也不少初學者(大概學一兩個月且未開始準備求職)談論要去刷 Leetcode,左思右想總覺得很怪,是我的問題還是這件事本身就有問題有時也不太確定,我的想法是,要學好一個語言本身就需要時間累積,更遑論演算法、資料結構這些知識了,我們來用更科學務實的方式想想看,刷題目的是什麼?我想到的大概三種:

  • 求職:合理,因為有些公司就是會考,但初學者不太會馬上遇到求職,所以等快求職再開始準備也不遲,那怎樣算快求職?因人而異,我認為提前 1 個月還算合理,假設每天做個 2 題也 60 題了,公司不會想考初學者太難
  • 精進自我:合理,但這屬於已有一定基礎者才適合做,因為若為初學者,肯定有更基礎的知識需要補足,而不是刷題
  • 單純好玩:稍微合理,認為解題超好玩的一定大有人在,但這通常不會是初學者,一般狀況是想很久不確定怎麼解,鬼打牆甚至牆打破仍想不出個所以然,而一般人應該不會覺得經歷這個過程只單純為了好玩,後面原因不外乎就是求職

因此想完會發現,這三種狀況都合理,但都不太會出現在初學者身上,也就是說,聽到初學者說要刷題有很高機率是不合理的,至少對我而言。因此回到那個問題,為什麼不先做 Leetcode?先學走再學跑,僅此而已。當然,天縱英才者不在此限。

另外,常常聽到有人在討論有些公司會直接用 Leetcode 題目來面試,或是爭論刷題強者實務表現真的就厲害嗎,諸如此類,會問這類問題不外乎看到別人比自己會刷題就拿到 offer 心理不平衡,或是看到同事透過刷題進來但表現差強人意,或單純對這件事不爽,如果是以前的我說不定也會去討論這種事,現在我已經覺得還好了,為什麼?只要站在公司立場想就能理解,這可以講很久這裡先講簡單結論,大多是 CP 值的問題,假設今天你創業當老闆你會用什麼方式挑選工程師?不要用學生做報告的角度思考,要想像你真的創業,有一堆事情要顧,在考量各種成本效益的情況下要怎麼選?再來,當公司成長到跨國企業,又該怎麼選?其實常常進行這種思考訓練會發現有些原本直覺奇怪的事情是某種折衷下的合理結果,不過很多事可能也是要實務遇過才能體會

想像個情境,如果今天主流大公司開出薪資優渥工程師職缺,但其中一項選才標準是,只有夠會煮佛跳牆的人才有資格進去,沒錯,就是煮出一甕好吃的佛跳牆,別人煮不煮我是不知道,但我會開始學習怎麼煮佛跳牆。

這個例子可以讓人很好理解我想傳達的概念,公司有權利選擇徵才方式,哪怕是煮佛跳牆。蛤?你問煮佛跳牆跟軟體工程師有什麼關聯性?會煮佛跳牆就代表實務能力強嗎?我哪知道,你管他為什麼,老闆想這麼做有他的原因,如果用煮佛跳牆選才導致公司進來一堆不適任員工那老闆就要自己承擔公司倒閉風險,但我們要不要去應徵則可以自己決定,看不慣許多公司用煮佛跳牆來選人那你可以選擇不加入這場遊戲。所以這就是為什麼我已經不會去討論用刷題徵才是否合適,我會去想,這些公司我想不想加入?想加入的話那怎麼把題刷好?其實就這麼簡單

Codewars 題目精彩回顧

說精彩回顧只是想模擬影片精彩回顧,這裡只挑一題分享,等下次積分到 200 分、突然想到、或單純沒東西寫再來挑題目分享XD

創作者:emporio

題目:Sum of two lowest positive integers

描述:

Create a function that returns the sum of the two lowest positive numbers given an array of minimum 4 positive integers. No floats or non-positive integers will be passed.

For example, when an array is passed like [19, 5, 42, 2, 77], the output should be 7.

[10, 343445353, 3453445, 3453545353453] should return 3453455.

我當時的答案

function sumTwoSmallestNumbers(numbers){
let newNumbers = JSON.parse(JSON.stringify(numbers))
let numMin = []
function findMin() {
let index = newNumbers.findIndex(e => e === Math.min(...newNumbers))
numMin.push(newNumbers[index])
newNumbers.splice(index, 1)
}
findMin()
findMin()
return numMin.reduce((a, b) => a + b)
};

思考邏輯很簡單,先找到最小元素,push 到新陣列後,再重複一次這個動作,接著把新陣列兩個元素相加即可,寫完後覺得這個答案很冗,光是看到裡面 findMin() 要重複 2 次就知道一定有地方要改,但我滿喜歡這個答案,因為一次複習很多語法,光是一開始複製陣列就踩到坑,原來拷貝還有深淺之分,另外也複習了 findIndex()、Math.min()、splice()、reduce() 等等,算是 CP 值很高的一題

其他看到不錯的解法

在 Codewars 提交答案後能看到其他人的解法,可以針對某解法投票 Best Practices 或 Clever,系統會依照 Best Practices 投票數排序列出其他人解法,這題前幾名的主要都是跟 sort() 應用有關,這邊也是一個坑,原來背後排序有這麼多學問,目前研究了一些,關於 sort() 之後再寫一篇不然這篇會太長,以下挑三個寫法分享

⭐方法一

function sumTwoSmallestNumbers(numbers){  
numbers = numbers.sort(function(a, b){return a - b; });
// 其實可以寫得更簡潔
// numbers = numbers.sort( (a, b) => a - b )
return numbers[0] + numbers[1];
};

利用 sort() 方法將陣列從小到大重新排序後,將第 0 個與第 1 個元素相加,要注意,sort() 會直接影響原陣列

⭐方法二

function sumTwoSmallestNumbers(numbers) {  
var [ a, b ] = numbers.sort((a, b) => a - b)
return a + b
}

跟方法一邏輯一樣,只是較簡潔,直接指定一個變數為內含兩個元素的陣列

⭐方法三

const sumTwoSmallestNumbers = numbers => numbers.sort((x, y) => x - y).slice(0, 2).reduce((x, y) => x + y);

sort() 完後直接用 slice() 取出前兩個元素的陣列,再用 reduce() 把兩個元素相加

結尾

初學者建議先從 Codewars 開始玩,累積一段時間再去刷 Leetcode,這樣效益比較高,至少我現在是這樣做,或許等學到一定程度再來分享這種方式的成效會更有說服力,先祝大家學習順利。

--

--

Johnny Fang

把 Medium 當 Notion 用,寫一下 coding 學習筆記 | email: johnny781222@gmail.com | LinkedIn: www.linkedin.com/in/johnny-fang-9356b2156 | Discord 使用者名稱:johnnyfang