GitHub Repo:coderZsq.target.swift
Follow: coderZsq · GitHub
Resume: coderzsq.github.io/coderZsq.we…
日常扯淡
前段日子寫了篇面經, 得到了掘金
的徵文活動
的三等獎
還是非常開心, 但是被寒神說面試不過就洩題
, 影響不好, 我想想也是, 馬上就把大廠的名字給抹掉
了, 但被轉載的就無能為力
了, 看到下面好多噴的, 真是背後一涼, 一首涼涼送給自己, 造成的傷害無法挽回, 再此鄭重道歉, 再也不寫面經了. 希望以後還能獲得大廠面試
的機會!
被大廠
刷掉後, 我心情難以平復, 因為其實我是做了充足的準備
來的, 但是還是實力上有差
距, 誒... 還是想想改如何提升
自己的水平吧!
其實對於自己, 我其實並不知道iOS
該如何進行學習, 也不知道技術這條路
我半道轉的是不是正確, 更不知道在當今環境下我這種水平的iOS
開發者是否還有存在的必要.
說起轉行做iOS
到現在, 從Objective-C
基礎語法學起, 認為OC
是最好的語言, 學會做幾個簡單的UITableView
頁面, 能播放音訊視訊
, 覺得自己真是轉了個高大上的職業
, 現在想想, 真是膚淺
的讓人忍不住發笑.
我把做iOS
這段學習經歷分為五個階段:
第一個階段: 是剛剛轉行進入網際網路企業, 那時候覺得學好CALayer
掌握了一些酷炫的動畫(貝塞爾曲線
), 感覺就已經比大多數人都強了, 那時候還把常用的工具類封裝起來, 就覺得, 嗯, 自己還不錯.
第二個階段: 就是瞎學些有的沒的, 如Swift
, JavaScript
, Java
什麼的, 覺得自己全棧了, 什麼都會了, 很牛逼, 你看還自己能夠寫一個前端簡歷, 還做過公司的前端專案, 伺服器開發也會了, 自信心爆棚啊, 覺得自己真是無所不能.
第三個階段: 寫了一個架構生成器
, 覺得自己所向披靡
了, 公司專案都在用我寫的架構, 用著我制定的規則
, 尼瑪不就是一個簡單的字串替換
, 嘚瑟個啥... 而且對於架構的理解膚淺至極
...
第四個階段: 察覺到了自己的薄弱, 開始學習iOS
的底層原理, 學習了C\C++
的語法, 學習了Linux
基礎, 學習了8086, ARM64
彙編, 瞭解了一些自認為
還是比較深的知識點. 覺得自己前途還是有希望的.
第五個階段: 也就是被刷掉之後的現在, 其實我現在也很迷茫, 也不知道現在學的東西到底有沒有用, 也不知道大廠到底要什麼樣的人才(只知道牛逼就行...), 但我也通過把知識點進行分類進行進階吧. 把最近的學習總結分享出來和大家一起討論, 像我這種水平的玩家到底該怎麼玩耍.
學習計劃
我把最近學習的方面全部都整理在StudyNote
這個裡面了, 可以看得出, 這個階段, 我明顯的就是像要學習加強演算法與資料結構
方面的, 可能是因為被大廠刷的有陰影了吧.
除了資料結構與演算法
, 最近剛看完的CS193p
的教程, 白鬍子老頭的這個視訊的質量很高
, 發現了很多我平時忽略的東西, 而且覺得很多我以為的寫法從本質上
都是有問題的.
還有就是objc.io
的這幾本書了, 質量挺高的, 函式式Swift
的學習對我很有幫助. 感覺就像是開啟了新世界.
還有就是排在後面的學習計劃: python
, 資料分析
, 機器學習
, 深度學習
.可以看得出, 其實這些都學完, 其實也不知道能夠幹什麼, 無所事事誒... 但至少這些都是我能夠找到的比較高質量
的資料了, 如果有其他高質量的資料, 歡迎進行資料共享hhhh~
演算法學習
果然, 演算法是擋在(複製黏貼
)程式設計師和(正常
)程式設計師之前的一條很難跨越的鴻溝, 我這裡不是說, 你會寫快速排序
,二分查詢
, 深度廣度優先
這類大家都能夠背出來的東西就叫做掌握演算法了, 我在Coursera
上看了加州大學
的演算法課, 才知道該如何設計演算法, 但由於是英語的, 而且沒有程式碼, 純數學的看不太懂, 所以轉向了北京大學
的演算法課, 以下就是我最新學到的演算法和你分享.
這個只是演算法基礎
的第二節課, 你能想象這是演算法基礎麼? 光是這個題目, 我就看了老半天, 大意是點選一個燈, 上下左右的燈會自動點亮(熄滅)
, 當隨機給出點亮熄滅數的時候, 需要算出點選哪幾個燈可以將所有的燈點亮或熄滅.
這種演算法題, 真是聞所未聞見所未見吧
, 而且這是演算法基礎的開頭的課... 我在想難道那些大廠的人做這種題跟玩的一樣
麼, 想起了面試官的微笑
, 那可真是有力量的微笑呢.
#include <stdio.h>
int puzzle[6][8], press[6][8];
/*
推測驗證過程:
根據第一行猜測
*/
bool guess() {
int c, r;
//根據press第1行和puzzle陣列,計算press其他行的值
for(r=1; r<5; r++) {
for(c=1; c<7; c++) {
press[r+1][c]=(puzzle[r][c]+press[r][c]+press[r-1][c]+press[r][c-1]+press[r][c+1])%2;
}
}
//判斷所計算的press陣列能否熄滅第5行的所有燈
for(c=1; c<7; c++) {
if ((press[5][c-1]+press[5][c]+press[5][c+1]+press[4][c])%2 != puzzle[5][c]) {
return false;
}
}
return true;
}
/*
列舉過程:
對press第1行的元素press[1][1]~press[1][6]的各種取值進行列舉
*/
void enumerate() {
int c;
bool success; //這個變數時當時定義了沒排上用場吧,NodYoung注
for(c=1; c<7; c++) {
press[1][c]=0;
}
while(guess()==false) {
press[1][1]++;
c=1;
while(press[1][c]>1) { //累加進位
press[1][c]=0;
c++;
press[1][c]++;
}
}
return ;
}
int main() {
int cases, i, r, c;
scanf("%d", &cases);
for(r=0; r<6; r++) {
press[r][0]=press[r][7]=0;
}
for(c=0; c<7; c++) {
press[0][c]=0;
}
for(i=0; i<cases; i++) {
for(r=1; r<6; r++) {
for(c=1; c<7; c++) {
scanf("%d", &puzzle[r][c]); //讀入輸入資料
}
}
enumerate();
printf("PUZZLE#%d\n", i+1);
for (r=1; r<6; r++) {
for (c=1; c<7; c++) {
printf("%d ", press[r][c]);
}
printf("\n");
}
}
return 0;
}
複製程式碼
這是北大老師
視訊裡給出的演算法的答案, 講的很好, 但是說真的聽的是一知半解, 原因在於不知道為什麼, 這些網課都不是線上編譯
的, 而是直接對著程式碼分析
, 上面的程式碼摘抄自-> 可以搜尋熄燈問題
.
var puzzle = [[Int]](repeating: [Int](repeating: 0, count: 8), count: 6)
var press = [[Int]](repeating: [Int](repeating: 0, count: 8), count: 6)
複製程式碼
func guess() -> Bool {
for r in 1..<5 {
for c in 1..<7 {
press[r + 1][c] = (puzzle[r][c] + press[r][c] + press[r - 1][c] + press[r][c - 1] + press[r][c + 1]) % 2
}
}
for c in 1..<7 {
if (press[5][c - 1] + press[5][c] + press[5][c + 1] + press[4][c]) % 2 != puzzle[5][c] {
return false
}
}
return true
}
複製程式碼
func enumerate() {
var c = 1
for _ in 1..<7 {
press[1][c] = 0
while (guess() == false) {
press[1][1] += 1
c = 1
while press[1][c] > 1 {
press[1][c] = 0
c += 1
press[1][c] += 1
}
}
c += 1
}
}
複製程式碼
class Enumerate {
static func main() {
let cases = 1
for r in 0..<6 {
press[r][0] = 0
press[r][7] = 0
}
for c in 1..<7 {
press[0][c] = 0
}
for i in 0..<cases {
for r in 1..<6 {
for c in 1..<7 {
puzzle[r][c] = 2.arc4random
}
}
enumerate()
print("PUZZLE #\(i + 1)")
for r in 1..<6 {
for c in 1..<7 {
print(puzzle[r][c], terminator: "")
}
print()
}
print("== press ==")
for r in 1..<6 {
for c in 1..<7 {
print(press[r][c], terminator: "")
}
print()
}
print()
}
}
}
複製程式碼
以上是我學習的時候轉換成swift
表達的, 不為什麼, 只是用來熟悉Swift
語法罷了, 畢竟OC
也不知道還能活個幾年了.
PUZZLE #1
011010
001110
010011
000101
100000
== press ==
001001
000101
001010
001101
011110
複製程式碼
但是光看程式碼, 很難看懂這個結果到底是正確還是不正確的... 因為跑出來是這樣的一個東西, 但是有些地方還是可以講一下, 比如是外面包了一圈0
來避免冗餘邏輯判斷, 用2進位制
進位的方法進行運算, 還是有學到一些皮毛的.
看得見的演算法
當然, 這種文字上的描述, 很難有深刻的印象的, 所以, 我就在想是否可以把這個熄燈遊戲給做出來, 再自己測試一下呢? 想到就幹吧!!
通過CS193p
的學習, 對於畫UI
方面有了全新的認識, 該如何新增約束, MVC
到底怎麼寫, 以至於我以前理解的感覺完全就是錯的, 正好趁這個機會來練練手.
我們通過StoryBoard
先把View
畫好, 不得不說UIStackView
真是好用到爆!!
import Foundation
struct Matrix {
var rows: Int
var columns: Int
}
struct LightSwitch {
private var puzzle: [[Int]]
private var matrix: Matrix
var lights = [Int]()
mutating func lightUp(index: Array<Any>.Index?) {
guard let index = index else { return }
var m = Matrix(rows: 0, columns: 0)
if index <= matrix.rows {
m.columns = index + 1
} else {
m.columns += index % matrix.columns + 1
}
for i in 0...index {
if i % matrix.columns == 0 {
m.rows += 1
}
}
puzzle[m.rows][m.columns] = puzzle[m.rows][m.columns] == 0 ? 1 : 0
puzzle[m.rows + 1][m.columns] = puzzle[m.rows + 1][m.columns] == 0 ? 1 : 0
puzzle[m.rows][m.columns + 1] = puzzle[m.rows][m.columns + 1] == 0 ? 1 : 0
puzzle[m.rows - 1][m.columns] = puzzle[m.rows - 1][m.columns] == 0 ? 1 : 0
puzzle[m.rows][m.columns - 1] = puzzle[m.rows][m.columns - 1] == 0 ? 1 : 0
lights.removeAll()
for r in 1..<matrix.rows + 1 {
for c in 1..<matrix.columns + 1 {
lights.append(puzzle[r][c])
}
}
}
init(matrix: Matrix) {
self.matrix = matrix
puzzle = [[Int]](repeating: [Int](repeating: 0, count: matrix.columns + 2), count: matrix.rows + 2)
for r in 1..<matrix.rows + 1 {
for c in 1..<matrix.columns + 1 {
puzzle[r][c] = 2.arc4random
lights.append(puzzle[r][c])
}
}
print("========")
for r in 0..<matrix.rows + 2 {
for c in 0..<matrix.columns + 2 {
print(puzzle[r][c], terminator: "")
}
print()
}
print("========")
}
}
複製程式碼
Model
程式碼, 原來MVC
的M
需要這樣寫的, 以前都只是認為是簡單的資料結構來的
真是膚淺, 這種直接業務邏輯寫在M
裡面的的寫法真是好用到爆啊!
import UIKit
extension UIColor {
var toImage: UIImage {
let bounds = CGRect(origin: .zero, size: CGSize(width: 1.0, height: 1.0))
let renderer = UIGraphicsImageRenderer(bounds: bounds)
return renderer.image { context in
self.setFill()
context.fill(CGRect(x: 0.0, y: 0.0, width: 1.0, height: 1.0))
}
}
}
extension Int {
var arc4random: Int {
if self > 0 {
return Int(arc4random_uniform(UInt32(self)))
} else if self < 0 {
return -Int(arc4random_uniform(UInt32(self)))
} else {
return 0
}
}
}
class ViewController: UIViewController {
@IBOutlet var lights: [UIButton]! {
didSet {
for (index, light) in lights.enumerated() {
light.setBackgroundImage(UIColor.yellow.toImage, for: .normal)
light.setBackgroundImage(UIColor.darkGray.toImage, for: .selected)
light.isSelected = switchs.lights[index] == 1 ? true : false
}
}
}
@IBAction func lightUp(_ sender: UIButton) {
switchs.lightUp(index: lights.index(of: sender))
for (index, light) in lights.enumerated() {
light.isSelected = switchs.lights[index] == 1 ? true : false
}
if Set(switchs.lights).count == 1 {
let alert = UIAlertController(title: "Congratulation", message: "You made all light up successfully", preferredStyle: .alert)
alert.addAction(UIAlertAction(
title: "again",
style: .default,
handler: { [weak self] _ in
self?.restart()
}
))
present(alert, animated: true)
}
}
@IBAction func restart(_ sender: UIButton? = nil) {
switchs = LightSwitch(matrix: Matrix(rows: 5, columns: 6))
for (index, light) in (self.lights.enumerated()) {
light.isSelected = self.switchs.lights[index] == 1 ? true : false
}
}
var switchs: LightSwitch = LightSwitch(matrix: Matrix(rows: 5, columns: 6))
}
複製程式碼
Controller
的程式碼, 這才能夠真正理解什麼叫做控制器用來協調View
和Model
的互動, Model
和View
毫無關聯, 這才是iOS
的正確寫法啊.
執行了一下, 果然白鬍子大叔沒有騙我, 跑的6到飛起
~
可以看到的是, 終端列印的矩陣和介面上顯示的是一一對應的, 從北大老師
學到的外面包一圈的方法也是特別好用的.
演算法測試
init(matrix: Matrix) {
self.matrix = matrix
puzzle = [[Int]](repeating: [Int](repeating: 0, count: matrix.columns + 2), count: matrix.rows + 2)
for r in 1..<matrix.rows + 1 {
for c in 1..<matrix.columns + 1 {
// puzzle[r][c] = 2.arc4random
puzzle[1][1] = 1
lights.append(puzzle[r][c])
}
}
print("========")
for r in 0..<matrix.rows + 2 {
for c in 0..<matrix.columns + 2 {
print(puzzle[r][c], terminator: "")
}
print()
}
print("========")
}
複製程式碼
我們將初始狀態從隨機數改成只暗一個燈, 位置是[1][1]
class Enumerate {
static func main() {
let cases = 1
for r in 0..<6 {
press[r][0] = 0
press[r][7] = 0
}
for c in 1..<7 {
press[0][c] = 0
}
for i in 0..<cases {
for r in 1..<6 {
for c in 1..<7 {
// puzzle[r][c] = 2.arc4random
puzzle[1][1] = 1
}
}
enumerate()
print("PUZZLE #\(i + 1)")
for r in 1..<6 {
for c in 1..<7 {
print(puzzle[r][c], terminator: "")
}
print()
}
print("== press ==")
for r in 1..<6 {
for c in 1..<7 {
print(press[r][c], terminator: "")
}
print()
}
print()
}
}
}
複製程式碼
我們把北大演算法
也改成對應的[1][1]
PUZZLE #1
100000
000000
000000
000000
000000
== press ==
000111
101010
101100
001000
110000
複製程式碼
可以看懂只要按下全部位置對應為1
的按鈕就可以將所有燈都開啟了, 我們來試一下.
更新演算法
為了不用每次都跑到另一個程式去執行演算法, 我新增了Hint
提示功能, 每盤遊戲都可以點選提示, 看到提示顯示的深色按鈕點選對應位置的燈, 即可點亮所有的燈.
新增這個功能其實也很簡單, 只需要新建一個Popver
控制器即可, 使用對應演算法, 對映到向量
即可.
import UIKit
class HintViewController: UIViewController {
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let fittedSize = topLevelView?.sizeThatFits(UILayoutFittingCompressedSize) {
preferredContentSize = CGSize(width: fittedSize.width + 30, height: fittedSize.height + 30)
}
}
override func viewDidLoad() {
super.viewDidLoad()
if presentationController is UIPopoverPresentationController {
view.backgroundColor = .clear
}
}
@IBOutlet var hints: [UIButton]! {
didSet {
for (index, hint) in hints.enumerated() {
hint.setBackgroundImage(UIColor.yellow.toImage, for: .normal)
hint.setBackgroundImage(UIColor.darkGray.toImage, for: .selected)
hint.isSelected = switchs?.hints[index] == 1 ? true : false
}
}
}
@IBOutlet weak var topLevelView: UIStackView!
var switchs: LightSwitch?
}
複製程式碼
新增控制器, 就是顯示提示的控制器
@IBOutlet weak var hintButton: UIButton!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Show Hint", let destination = segue.destination.contents as? HintViewController,
let ppc = destination.popoverPresentationController {
ppc.delegate = self
ppc.sourceRect = hintButton.bounds
destination.switchs = switchs
}
}
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
return .none
}
複製程式碼
原先控制器的Segue
設定, 讓iphone
預設不適配
mutating func guess() -> Bool {
for r in 1..<matrix.rows {
for c in 1..<matrix.columns + 1 {
press[r + 1][c] = (puzzle[r][c] + press[r][c] + press[r - 1][c] + press[r][c - 1] + press[r][c + 1]) % 2
}
}
for c in 1..<matrix.columns + 1 {
if (press[matrix.rows][c - 1] + press[matrix.rows][c] + press[matrix.rows][c + 1] + press[matrix.rows - 1][c]) % 2 != puzzle[matrix.rows][c] {
return false
}
}
return true
}
mutating func enumerate() {
var c = 1
for _ in 1..<matrix.columns + 1 {
press[1][c] = 0
while (guess() == false) {
press[1][1] += 1
c = 1
while press[1][c] > 1 {
press[1][c] = 0
c += 1
press[1][c] += 1
}
}
c += 1
}
}
複製程式碼
在Model
中加入核心演算法即可
這個Demo
可能是全網唯一的熄燈問題
的UI
版本吧, 給自己一個贊~
經過好幾次測試, 可以看見, 演算法是正確
的, 我也學到了這個演算法背後的思維
, 更通過了寫了一個Demo
來證明了演算法的正確性. 這個Demo
的難點在於向量
和矩陣
之間的互相轉換, 這裡為什麼不說一維陣列
和二維陣列
呢? , 原因在於吳恩達
的機器學習課程中也教會了我一些比較厲害的演算法, 比如梯度下降
之類的.
好了, 現在寫文章沒有之前頻繁了, 原因在於之前那些文章都太水
, 太膚淺
, 寫了對自己也沒有太大的意義, 被人看到也只會覺得是垃圾而已... 所以在我第五階段的學習
後, 希望能夠有機會進入一家大廠繼續深造吧!
最後 本文中所有的原始碼都可以在github上找到:
GitHub Repo:coderZsq.target.swift
Follow: coderZsq · GitHub
Resume: coderzsq.github.io/coderZsq.we…