1. 程式人生 > >Swift面向協議程式設計(附程式碼)

Swift面向協議程式設計(附程式碼)

什麼是swift協議?

Protocol
Swift標準庫中有50多個複雜不一的協議,幾乎所有的實際型別都是媽祖若干協議的。protocol是Swift語言的底座,語言的其他部分正是在這個底座上組織和建立起來的。這和我們熟知的面向物件的構建方式很不一樣。

一個最簡單但是有實際用處的Swift協議定義如下:

protocol Greetable {
    var name: String{get}
    func greet()
}

這幾行程式碼定義了一個名為Greetable的協議,其中有一個name屬性的定義,以及一個greet方法的定義。

所謂協議,就是一組屬性和/或方法的定義,而如果某個具體型別想要遵守一個協議,那它需要實現這個協議所定義的所有這些內容。協議實際上做的事情不過是“關於實現的約定”。

面向物件

在深入Swift協議的概念之前,我想先重新讓大家回國一下面向物件。相信我們不論在教科書或者是部落格等各種地方對這個名詞都十分熟悉了。那麼又一個很有意思,但是其實並不是每個程式設計師都想過的問題,面向物件的核心思想究竟是什麼?

class Animal {
    var leg:Int {return 2}
    func eat() {
        print("eat food")
    }
    func run() {
    print("run with \(leg)legs")    
    }
}

class Tiger:Animal {
override var leg: Int {return 4} override func eat() { print("eat meat") } } let tiger = Tiger() tiger.eat()//eat meat tiger.run()//run with 4 legs

父類Animal 定義了動物的leg,以及動物的eat和run方法,並未她們提供了實現。子類的Tiger根據自身情況充血了leg和eat,而對於run,父類的實現已經滿足要求,因此不必重寫。

我們看到Tiger和Animal共享了一部分程式碼,這部分程式碼還被封裝到了父類中,而除了Tiger的其他的字類也能夠使用Animal的這些程式碼。這其實就是OOP的核心思想-使用封裝和整合,將一些列相關的內容放到一起。我們的前輩們為了能夠對真實的世界的物件進行建模,發展出了面向物件程式設計的概念,但是這套理念有一些缺陷。雖然我們努力用這套抽象和整合的方法進行建模,但是實際的食物往往是一系列特質的組合,而不單單是以一脈相承並逐漸擴充套件的方式構建的。所以最近大家越來越發現面向物件很多時候其實不能很好的對食物進行抽象,我們可能需要尋找另一個更好的方式。

面向物件程式設計的困境

橫切關注點

我們再來看一個例子。這次讓我們遠離動物界,回到Cocoa,假設我們又一個VIewController,它整合自UIViewController,我們向其中新增一個myMethod:

class ViewControllerUIViewControlller{
    func myMethod() {
    }
}

如果這個時候我們又有一個繼承自UITableviewController的AntherViewController,我們也想向其中新增同樣的myMethod:

class AnotherViewControllerUITableViewController{
    func myMethod(){
    }
}

這是我們迎來了OOP的第一大困境,那就是我們很難再不同整合關係的類裡共用程式碼。這裡的問題用“行話”來說叫做“橫切關注點(cross-cutting concerns)”。我們的關注點myMethod位於兩條繼承鏈的橫切面上。面向物件是一種不錯的抽象方式,但是肯定不是最好的方式。它無法描述兩個不同食物具有某個相同特性這一點。在這裡,特性的組合要比整合更貼切事物的本質。

想要解決這個問題,我們有幾個方案:

  • Copy&Paste
    這是一個比較糟糕的解決方案,但是演現場還是有不少朋友選擇了這個方案,特別是在工期很緊,無暇優化的情況下。這誠然可以理解,但是這也是壞程式碼的開頭。我們儘量避免這種做法。
  • 引入BaseViewControlller
    在一個整合自UIViewController的BaseViewControlller上新增需要共享的程式碼,或者乾脆在UIViewController上新增extension。看起來這是一個稍微靠譜的做法,但是如果不斷這麼做,會讓所謂的Base很快變成垃圾堆。指責不明確,任何東西都扔進Base,你完全不知道那些類走了Base,而這個超級類對程式碼的影響也會不可估量。
  • 依賴注入
    通過外界傳入一個帶有myMethod的物件,用新的型別來提供這個功能。這是一個稍好的方式,但是引入額外的依賴關係,坑能也是我門不太願意看到的。
  • 多繼承
    當然,Swift是不支援多繼承的,不過如果有多整合的話,我們確實可以從多個父類進行整合,並將myMethod新增到合適的地方。有一些語言選擇了支援多整合,但是它會帶來OOP中另一個著名的問題:菱形缺陷

菱形缺陷

上面的例子中,如果我們有多繼承,納悶ViewController和AnotherViewController的關係可能會是這樣的:
這裡寫圖片描述
在上面這種拓撲結構中,我們只需要在ViewContorller中實現myMethod,在AnotherVIewController中也就可以繼承並使用它了。看起來很完美,我們避免了重複。但是多整合有一個無法迴避的問題,就是兩個父類都實現了同樣的方法時,自樂該怎麼辦,我們很難確定因該繼承哪一個父類的方法。因為多整合的拓撲結構是一個菱形,所以這個問題又被叫做菱形缺陷。像是C++這樣的語言選擇粗暴的將菱形缺陷的問題交給程式設計師處理,這無疑非常複雜,並且增加了人為錯誤的可能性。二絕大多數現代語言對多整合這個特性選擇避而遠之。

動態派發安全性

OC恰如其名,是一門典型的OOP語言,同時它繼承了SmallTalk的訊息傳送機制。這套機制十分靈活,是OC的基礎思想,但是有時候相當危險。

ViewController *v1 = ...
[v1 myMethod];
AnotherViewController *v2 = ...
[v2 myMethod];

 NSArray *array = @[v1,v2];
 for (id obj in array){
 [obj myMethod];
}

我們如果在ViewController和AnotherViewController中都實現了myMethod的話,這段程式碼是沒有問題的。myMethod將會唄動態傳送個array中的v1和v2。但是,要是我們有一個沒實現myMethod的型別,會如何?

NSObject *v3 = [NSObject new];
NSArray *array = @[v1,v2,v3];
for (id obj in array){
    [obj myMethod];
};

編譯依然可以通過,但是顯然,程式將在執行時崩潰。OC是不安全的,編譯器預設你知道某個方法確實有實現,這是訊息傳送的靈活性所必需付出的代價。而在app開發看來,用可能的崩潰來換取靈活性,顯然這個代價太大了。肅然這不是OOP正規化的問題,但是它確實在OC時代給我們帶來了切膚之痛。

三大困境

我們可以總結一下OOP面臨的這幾個問題。

  • 動態派發安全性
  • 橫切關注點
  • 菱形缺陷

    首先,在OC中動態派發讓我們承擔了在執行時才發現錯誤的風險,這很很有可能是發生在上線產品中的錯誤。其次,橫切關注點讓我們難以對物件進行完美的緘默,程式碼的重用也會更加糟糕

協議擴充套件和麵向協議程式設計

使用協議解決OOP困境

協議並不是什麼新東西,也不是Swift的發明。在Java和C#裡,它叫做Interface。二Swift中的Protocol將這個概念繼承了下來,併發揚光大。讓我們回到一開始定義的那個簡單協議,並嘗試實現這個協議:

protocol    Greetable{
     var name : String{get}
     func greet()
}
struct Person:Greetable{
    let name :String
    func greet(){
    print("你好\(name)")
    }
}
Person(name:"zq").greet()

實現很簡單,Person結構體通過實現name和greet來滿足Greetable。在呼叫時,我們就可以使用Greetable中定義的方法了。

動態派發安全性

除了Person,其他型別也可以實現Greetable,比如Cat:

struct Cat: Greetable {
    let name: String
    func greet() {
    pring("meow~\(name)")
    }
}

現在,我們就可以將協議作為標準型別,來對方法呼叫進行動態派發了:

let array: [Greetable] = [
    Person(name:"zq");
    Cat(name:"zz")]
for obj in array{
    obj.greet()
}

對於沒有實現Greetbale的型別,編譯器將返回錯誤,因此不存在訊息錯誤傳送的情況

橫切關注點

使用協議和協議擴充套件,我們可以很好的共享程式碼。回到上一節的myMethod方法,我們來看卡如何使用協議來搞定它。首先,我們可以定義一個含有myMethod的協議:

protocol P {
    func myMethod()
}   

注意這個協議沒有提供任何實現。我們依然需要在實際型別遵守這個協議的時候為它提供具體的實現:

extension ViewController: P {
    func myMethod() {
    doWork()
    }
}

extension AnotherViewController: P {
    func myMethod() {
    doWork()
    }
}

這和Copy&Paste也沒什麼不同啊?沒有!聽說過協議擴充套件嗎 繼續看

extension P {
    func myMethod() {
        doWork()
    }
}

給協議做擴充套件,有了這個擴充套件後我們只需要簡單的宣告ViewController和AnotherViewController遵守P,就可以直接使用myMehtod的實現了:

extension ViewController: P {}
extension AnotherViewController: P {}

不僅如此,除了已經定義過的方法,我們甚至可以在擴充套件中新增協議裡沒有定義過的方法。在這些額外的方法中,我門可以依賴協議定義過的方法進行操作。

  • 協議定義
    ·提供實現的入口
    ·遵循協議的型別需要對其進行實現
  • 協議擴充套件
    ·為入口提供預設實現
    ·根據入口提供額外實現
    這樣一來,橫切關注的問題也簡單安全的得到了解決。

菱形缺陷

最後我們看看多繼承。多整合中存在的一個重要問題是菱形缺陷,也就是字類無法確定使用哪個負累的方法。在協議的對應方面,這個問題雖然依然存在,但卻是可以為宜安全的確定的。

protocol Nameable {
    var name: String{get}
}
protocol Identifiable {
    var name: String{get} 
    var id: Int {get}
}
如果有一個型別,需要同時實現兩個協議的話,他必須提供一個name屬性,來同時滿足兩個協議的要求

struct Person: Nameable, Identifiable {
let name :String
let id: Int
}

這樣以來菱形缺陷的問題就基本得以解決

#在日常開發中使用協議


##基於Protocol的網路請求
網路請求層是實踐POP的一個理想場所。我們在接下的例子中將從零開始,用最簡單的面向協議的方式向偶見一個不那麼完美的網路請求和模型層,他肯呢個包含一些不合理的設計和耦合,但是卻是起步最容易得到的結果。然後我們逐步捋順各部分的所屬,並用分離職責的方式來進行重構。最後我們會為這個網路請求層進行測試。通過這個例子,我希望能夠設計出包括型別安全,解耦合,易於測試和良好的擴充套件性等諸多優秀特性在內的POP程式碼。

 1. 初步實現

首先我們想要做的事情是從一個API請求一個JSON,然後將它轉換成為Swift中可用的例項。作為例子的API非常簡單,你可以直接訪問https://api.onevcat.com/users/onevcat 來檢視返回

{“name”:”onevcat”,”message”:”Welcome to MDCC 16!”}

我們新建一個專案並使用struct建立一個模型,為什麼要使用struct呢?

// User.swift
import Foundation

struct User {
let name: String
let message: String

init?(data: Data) {
    guard let obj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
        return nil
    }
    guard let name = obj?["name"] as? String else {
        return nil
    }
    guard let message = obj?["message"] as? String else {
        return nil
    }

    self.name = name
    self.message = message
}

}

User.init(data:)將輸入的data資料解析為JSON物件然後取出name和message,並構建代表API返回的User例項,非常簡單。

現在讓我們來看看有趣的部分,也就是如何使用POP的方式從URL請求資料,並聲稱對應的User。首先,我們可以建立一個protocol來代表請求。對於一個請求,我們需要知道它的請求路徑,HTTP方法,所需要的引數等資訊。一開始這個協議可能是這樣的:

enum HTTPMethod: String {
case GET
case POST
}

protocol Request {
var host: String { get }
var path: String { get }

var method: HTTPMethod { get }
var parameter: [String: Any] { get }

}

現在新建一個UserRequest來實現Request協議:

struct UserRequest: Request {
let name: String

let host = "https://api.onevcat.com"
var path: String {
    return "/users/\(name)"
}
let method: HTTPMethod = .GET
let parameter: [String: Any] = [:]

}

UserRequest中有一個未定義初始值的name屬性,其他的屬性都是為了滿足協議所定義的。因為請求的引數使用者名稱name會通過URL進行傳遞,所以parameter是一個空字典(因為要用get請求)。有了協議定義和一個滿足定義的具體請求,現在我們需要傳送請求。為了任意請求都可以通過同樣的方法傳送,我們將傳送方法定義在Request協議擴充套件上:

extension Request {
func send(handler: @escaping (User?) -> Void) {
// … send 的實現
}
}

在send(handler:)的引數重,我們定義了可逃逸的(User?)-> Void,在請求完成後,我們呼叫這個handler方法來通知呼叫者是否完成,如果一切正常,則將一個User例項返回,否則傳回nil。

我們想要這個send方法對於搜遊的Request都通用所以顯然回撥的引數不能是User。通過在Request協議中新增一個管理型別,我們可以將回調引數進行抽象。在Request最後新增:

protocl Request {
···
associatedtype Response
}

然後在UserRequest中,我們也相應的新增型別定義,以滿足協議:

struct UserRequest: Request {

typealias Response = User
}

現在,我們來重新實現send方法,我們可以用Response代替具體的User,讓send一般化。我們這裡使用URLSession來發送請求

extension Request {
func send(handler: @escaping (Response?) -> Void) {
let url = URL(string: host.appending(path))!
var request = URLRequest(url: url)
request.httpMethod = method.rawValue

    // 在示例中我們不需要 `httpBody`,實踐中可能需要將 parameter 轉為 data
    // request.httpBody = ...

    let task = URLSession.shared.dataTask(with: request) {
        data, res, error in
        // 處理結果
        print(data)
    }
    task.resume()
}

}

通過拼接host和path,可以得到API的entry point。根據這個URL建立請求,進行配置,生成data task並將請求傳送。剩下的工作就是將會掉中的data轉換成為合適的物件型別。並呼叫handler同志外部呼叫者。對於User我們知道可以使用User.init(data:),但是對於一般的Response,我們還不知道如何將資料專為模型。我們可以在Request裡定義一個pase(data:)方法,來要求滿足該協議的具體型別提供合適的實現。這樣一來,提供轉換方法的任務就被下放到了UserRequest

protocol Request {

associatedtype Response
func parse(data: Data) -> Response?
}

struct UserRequest: Request {

typealias Response = User
func parse(data: Data) -> User? {
return User(data: data)
}
}

有了data轉換為Response的方法後,我們就可以對請求的結果進行處理了:

extension Request {
func send(handler: @escaping (Response?) -> Void) {
let url = URL(string: host.appending(path))!
var request = URLRequest(url: url)
request.httpMethod = method.rawValue

    // 在示例中我們不需要 `httpBody`,實踐中可能需要將 parameter 轉為 data
    // request.httpBody = ...

    let task = URLSession.shared.dataTask(with: request) {
        data, _, error in
        if let data = data, let res = parse(data: data) {
            DispatchQueue.main.async { handler(res) }
        } else {
            DispatchQueue.main.async { handler(nil) }
        }
    }
    task.resume()
}

}

現在我們做一下請求

let request = UserRequest(name: “onevcat”)
request.send { user in
if let user = user {
print(“(user.message) from (user.name)”)
}
}

2.重構關注點分離
雖然能夠實現需求,但是上面的實現可以說非常糟糕。讓我們看看現在Request的定義和擴充套件

protocol Request {
var host: String { get }
var path: String { get }

var method: HTTPMethod { get }
var parameter: [String: Any] { get }

associatedtype Response
func parse(data: Data) -> Response?

}

extension Request {
func send(handler: @escaping (Response?) -> Void) {

}
}

這裡最大的問題在於,Request管理了太多的東西。一個Request應該做的事情應該僅僅是定義請求入口和期望的相應型別,而現在Request補光定義了host的值,還對如何解析資料瞭如指掌。最後send方法被綁死在了URLSession的實現上,而且是作為Request的一部分村子。這是很不合理的,因為這意味著我們無法在不更改請求的情況下更新發送請求的方式,它們被耦合在了一起,這樣的結構讓測試變得異常艱難,我們可能需要通過stub和mock的方式對請求攔截,然後返回構造的資料,這會用到NSURLProtocol的內容,或者是引入一些第三方的測試框架大大的增加了專案的複雜程度。在OC這一時期這可能是一個可選項,但是在Swift新時代,我們有好的多的方法來處理這件事情。

首先我們將send(handler:)從Request中分離出來。我們需要一個單獨的型別負責傳送請求。這裡機遇POP的開發方式,我們定義一個可以傳送請求的協議:

protocol Client {
func send(_ r: Request, handler: @escaping (Request.Response?) -> Void)
//編譯錯誤原因是,Request是含有“關聯型別”的協議,所以它並不能作為獨立的型別來使用,我們只能夠將它作為型別約束,來限制輸入引數request。
//關聯型別(associatedtype):我不知道具體型別是什麼,一些服從我的類,結構體,列舉會幫我實現這個細節
}

正確的方式

protocol Client {
func send(_ r: T, handler: @escaping (T.Response?) -> Void)
var host: String { get }
}

除了使用<T:Request>這個範型方式意外,我們還將host從Request移動到了Client裡,這是更適合他的地方。現在,我們可以把含有send的Request協議擴充套件刪除,重新建立一個型別滿足Client了。和之前一樣,他將使用URLSession來發送請求:

struct URLSessionClient: Client {
let host = “https://api.onevcat.com

func send<T: Request>(_ r: T, handler: @escaping (T.Response?) -> Void) {
    let url = URL(string: host.appending(r.path))!
    var request = URLRequest(url: url)
    request.httpMethod = r.method.rawValue

    let task = URLSession.shared.dataTask(with: request) {
        data, _, error in
        if let data = data, let res = r.parse(data: data) {
            DispatchQueue.main.async { handler(res) }
        } else {
            DispatchQueue.main.async { handler(nil) }
        }
    }
    task.resume()
}

}

現在傳送請求部分和請求本身分離開了,而且我們使用協議的方式定義了Client。除了URLSessionClient意外,我們還可以使用任意的型別來滿足這個協議,併發送請求。這樣網路層的具體實現和請求本身就不再想管了,我們之後在測試的時候會進一步看到這麼做所帶來的好處。

現在還有一個問題,Request的pase方法。請求不應該知道如何解析得到的資料,這項工作應該叫個Response來做。而現在我們沒有對Response進行任何限定。接下來我們將新增一個協議,滿足這個協議的型別將知道如何將一個data轉換為實際的型別:

protocol Decodable {
static func parse(data: Data) -> Self?
}

Decodable定義了一個靜態的parse方法,現在我們需要在RequestResponse關聯型別中為它加上這個限制,這樣我們可以保證所有的Response都可以對資料進行解析,遠了Request中的parse生命也就可以解除了。最終的Request協議

protocol Request {
var path: String { get }
var method: HTTPMethod { get }
var parameter: [String: Any] { get }

// associatedtype Response
// func parse(data: Data) -> Response?
associatedtype Response: Decodable

}

修改User滿足Decodable,並且修改上面URLSessionClient的解析部分的程式碼,讓它使用Response中的parse 方法

extension User: Decodable {
static func parse(data: Data) -> User? {
return User(data: data)
}
}

struct URLSessionClient: Client {
func send(_ r: T, handler: @escaping (T.Response?) -> Void) {

// if let data = data, let res = parse(data: data) {
if let data = data, let res = T.Response.parse(data: data) {

}
}
}

最後,將client中不再需要的host和parse等清理一下,一個型別安全,解耦合的面向協議的網路層就呈現在我們眼前了。想要呼叫UserRequest時,我們可以這樣寫

URLSessionClient().send(UserRequest(name: “onevcat”)) { user in
if let user = user {
print(“(user.message) from (user.name)”)
}
}

可以為URLSessionClient新增一個單例,或者為請求新增Promise的呼叫方式。在POP的組織下,這些改動都很自然,也不會牽扯到請求的其他部分。你可以用和UserRequest型別相似的方式,為網路層新增其他的API請求,只需要定義請求所必要的內容,而不用擔心會觸及網路方面的具體實現。

3.網路層測試
將Client生命為協議給我們帶來了額外的好處,那就是我們不在侷限於使用某種特定的技術(比如這裡的URLSession)來實現網路請求。利用POP,你只是定義了一個傳送請求的協議,你可以很容易的使用像是ADNetworking或者Alamofire這樣的成熟的第三方框架來構建具體的資料並處理請求的低層實現。我們甚至可以提供一組“虛假”對請求的響應,用來進行測試。這和傳統的stub&mock的方式在概念上時接近的,但是實現起來要簡單得多,也明確的多。這個就是載入一個本地檔案。

4.可擴充套件性因為高度耦合,這種基於POP的實現為程式碼的擴充套件提供了相對寬鬆的可能性。我們剛才已經說過,你不必自行去實現一個完整的Client,而可以依賴於現有的網路請求框架,實現請求傳送的方法即可。也就是說你也可以很容易的將某個正在使用的請求方式替換為另外的方式,而不會影響請求的定義和使用。類似的以使用任意的第三方JSON解析庫,來幫助我們迅速構建模型型別,這僅僅只需要實現一個將Data轉換為對應模型型別的方法即可。

#使用協議幫助改善程式碼
通過面向協議的程式設計,我們可以從傳統的整合商解放出來,用一種更靈活的方式,搭積木一樣對程式進行組裝。每個協議專注於自己的功能,特別得益於協議擴充套件,我們可以減少類和整合帶來的共享狀態的風險,讓程式碼更加清晰。高度的協議話有助於解耦合,測試以及擴充套件,而結合範型來使用協議,更可以讓我們免於動態呼叫和型別轉換的苦惱,保證了程式碼的安全性。


這段程式碼用的playground

import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
protocol Client {
func send(_ r:T,handler:@escaping(T.Response?)-> Void)

}
protocol Decodable {
static func parse (data:Data) -> Self?
}
struct User:Decodable {
let name:String
let message:String
static func parse(data: Data) -> User? {
return User.init(data:data)
}
init?(data:Data) {
guard let obj = try? JSONSerialization.jsonObject(with: data, options:[])as?[String:Any] else{
return nil
}
guard let name = obj?[“name”] as? String else{
return nil
}
guard let message = obj?[“message”] as? String else{
return nil
}
self.name = name
self.message = message
}

}

enum HTTPMethod:String{
case GET
case Post
}

protocol Request{
var host:String {get}
var path:String {get}
var method:HTTPMethod{get}
var parameter:[String:Any]{get}
associatedtype Response:Decodable

}
struct URLSessionClient: Client {
static func shareClient() ->URLSessionClient{

return self.init()
}
func send<T: Request>(_ r: T, handler: @escaping (T.Response?) -> Void) {
    let url = URL(string: r.host.appending(r.path))!
    var request = URLRequest(url: url)
    request.httpMethod = r.method.rawValue

    let task = URLSession.shared.dataTask(with: request) {
        data, _, error in
        if let data = data, let res = T.Response.parse(data: data) {
            DispatchQueue.main.async { handler(res) }
        } else {
            DispatchQueue.main.async { handler(nil) }
        }
    }
    task.resume()
}

}
struct UserRequest:Request {

let name: String
let host = "https://api.onevcat.com"
var path: String {
    return "/users/\(name)"
}
let method: HTTPMethod = .GET
let parameter: [String : Any] = [:]
typealias Response = User
func phase(data: Data) -> User? {
    return User.init(data: data)
}

}
URLSessionClient.shareClient().send(UserRequest.init(name: “onevcat”)) { (user) in
if let user = user {
print(“(user.name)from(user.message)”)
}
}

“`

相關推薦

Swift面向協議程式設計程式碼

什麼是swift協議? Protocol Swift標準庫中有50多個複雜不一的協議,幾乎所有的實際型別都是媽祖若干協議的。protocol是Swift語言的底座,語言的其他部分正是在這個底座上組織和建立起來的。這和我們熟知的面向物件的構建方式很不一樣。

手把手教你如何用Julia做GPU程式設計程式碼

  新智元報道  來源:nextjournal編輯:肖琴、三石【新智元導讀】本文旨在快速介紹GP

《Python程式設計從入門到實踐》第9章類課後習題程式碼

目錄 9-1(9-2) 餐館 9-3 使用者 9-4 就餐人數 9-5 嘗試登陸次數 9-6 冰淇淋小店 9-7 管理員 9-8 許可權 9-10 匯入Restaurant類 9-11 匯入Admin類 9-12 多個模組​ 9-1(9-2) 餐館

《Python程式設計從入門到實踐》第10章檔案和異常動手試一試答案程式碼

目錄 10-3 訪客 10-4 訪客名單 10-6 加法運算 10-7 加法計算器 10-8 貓和狗 10-9 沉默的貓和狗 10-3 訪客 #!/usr/bin/env python # -*- coding:utf-8 -*- user = input

《OpenCV3程式設計入門》——5.5.8 離散傅立葉變換綜合示例程式程式碼

綜合《OpenCV3程式設計入門》——5.5 離散傅立葉變換原理和 《OpenCV3程式設計入門》——5.5.2 離散傅立葉變換相關函式詳解兩篇文章對離傅立葉變換的詳細介紹,本篇將展示實現離散傅立葉變化的示例程式(本篇所涉及的所有知識均在上述兩篇博文裡有詳細解釋,請參考): //--------

Python進階:函數語言程式設計例項程式碼

上篇文章“幾個小例子告訴你, 一行Python程式碼能幹哪些事 -- 知乎專欄”中用到了一些列表解析、生成器、map、filter、lambda、zip等表達形式,這就涉及到了Python中關於函數語言程式設計(functional programming)的語法、函式等

keras面向小資料集的影象分類VGG-16基礎上fine-tune實現程式碼

參考譯文地址:http://keras-cn.readthedocs.io/en/latest/blog/image_classification_using_very_little_data/ 本文作者:Francois Chollet 概述 在本文中,將使用VGG-16模型提供一種面向小資料集(幾百

半邊資料結構與網格細分演算法Loop subdivision程式碼

網格細分的原理其實並不難理解,它的難點主要在於如何實現。在看過無數有原理無程式碼的部落格後,終於決定寫一寫我的實現方法,並附上程式碼供大家參考。c++寫的可能比較笨拙,望見諒。 1.半邊資料結構 很好理解,就是把網格的每一條邊分成兩個半邊,半邊是有方向的同一條邊的兩個半邊方向相反。並且一條邊

ROI Align 在 R-FCN 中的推廣:PSROI-Align程式碼

ROI Align 在 R-FCN 中的推廣:PSROI-Align(附程式碼) 1. Position Sensitive ROI-Pooling 簡介 原文:https://blog.csdn.net/Bruce_0712/article/details/80287355 原始碼解析

前端練級之路——面向物件程式設計閉包

今天,我們來一起學習學習閉包吧。閉包是JavaScript中的一個重點,也是一個難點,很多高階應用都要依靠閉包實現。最重要的是,在前端面試中十家有八家都會問到閉包的問題。很多人面試的時候就被閉包虐了無數次,面試官們總是喜歡換著花樣通過閉包來虐求職者,真懷疑是不是面試官都有一點虐待傾向。不過面試官們既

如何在python中實現整數的二進位制迴圈移位程式碼

【時間】2018.11.03 【題目】如何在python中實現整數的二進位制迴圈移位(附程式碼) 概述 在python中,可以通過<<以及>>運算子實現二進位制的左移位以及右移位,然而並沒有實現迴圈移位的運算子,暫時也找不到可以實現迴圈移位的函式,所以在本文中,主

在python中使用opencv將RGB影象轉換為HSV及YCrCb影象程式碼

【時間】2018.11.01 【題目】在python中使用opencv將RGB影象轉換為HSV及YCrCb影象(附程式碼) 目錄 概述 一、程式碼實現 二、執行結果 三、關於HSV及YCrCb的一點補充 3.1HSV顏色空間 3.2 YCRCBA顏色空間

VC++6.0下基於MFC框架利用CInternetSession和CHttpFile獲取網頁資料程式碼

例:從網站http://qq.ip138.com/weather/guangdong/GuangZhou.htm獲取近三天的日期、天氣、溫度、風向,程式碼如下: //新增標頭檔案 #include <afxinet.h> //獲取網路資料 void CSensorSysDlg:

獨家 | 手把手教你用Python進行Web抓取程式碼

作為一名資料科學家,我在工作中所做的第一件事就是網路資料採集。使用程式碼從網站收集資料,當時對我來說是一個完全陌生的概念,但它是最合理、最容易獲取的資料來源之一。經過幾次嘗試,網路抓取已經成為我的第二天性,也是我幾乎每天使用的技能之一。 在本教程中,我將介紹一個簡單的例子,說明如何抓取一個網站,

機器學習中的優化演算法程式碼

摘要 > 優化演算法指通過改善訓練方式,來最小化(或最大化)損失函式E(x) 區域性最優問題 區域性最優與鞍點。在神經網路中,最小化非凸誤差函式的另一個關鍵挑戰是避免陷於多個其他區域性最小值中。實際上,問題並非源於區域性極小值,而是來自鞍點,即一個維度向上傾斜且

神經網路之過擬合程式碼

摘要 監督機器學習問題無非就是“minimizeyour error while regularizing your parameters”,也就是在規則化引數的同時最小化誤差。最小化誤差是為了讓我們的模型擬合我們的訓練資料,而規則化引數是防止我們的模型過分擬合我們的訓練資料

神經網路之權重初始化程式碼

摘要 神經網路/深度學習模型訓練的過程本質是對權重進行更新,在對一個新的模型進行訓練之前,需要每個引數有相應的初始值。對於多層神經網路/深度學習而言,如何選擇引數初始值便成為一個值得探討的問題。本文從實現啟用值的穩定分佈角度來探討神經網路的效率優化問題 權重在

Android 載入或多次載入程式碼

       這篇帖子主要說一下列表載入的問題,上個星期開發了幾個列表,開發完以後發現有個bug,就是重複載入,而且載入完第一次以後,跳到第二個上面,然後就卡在第二次載入上,其實資料已經加載出來了,只需要退出本次載入就行,然後多次核對程式碼後發現是因為在一個列

Android 獲取位置資訊經緯度程式碼

        獲取位置資訊主要通過GPS和網路位置兩種方法,優先順序還是GPS,有點就不多說了,下面說一下我做的方法及附程式碼,有疑問可在下方留言。        思路便是GPS優先,但在GPS訊號弱的情況下采取拿

Android 動態獲取儲存、位置、電話的許可權程式碼

       今天客戶提出上傳資訊時需攜帶經緯度,且需要兩種獲取位置的方式;故經思考使用了GPS和網路獲取位置,但在經過實地測試的時候才發現沒寫獲取許可權。       便在登入介面加上獲取許可權程式碼,此次獲取的是儲存、位置