Android如何在kotlin中進行函數語言程式設計?kotlin版本原始碼解析

南方吳彥祖_藍斯發表於2021-09-30

前言

Android如何在kotlin中進行函數語言程式設計?kotlin版本原始碼解析

對於大多數應用程式開發人員來說,函數語言程式設計很難理解。固定的框架只會告訴程式設計師怎麼直接構建應app,從而減少了他們實驗各種不同程式設計方式的慾望。經常能聽到有人說只有“這樣”或者“那樣”才是構建應用程式的正確方法,但是改變這一程式設計的本性卻逐漸被人遺忘。

很多App開發人員就掉進了這種誤區。iOS 和 Android SDK 有很重的開發規範。導致開發人員會認為這是構建App的唯一方法。在本文中,我們將使用 Kotlin。Kotlin 可以在任何使用 Java 的地方使用。隨著 Android 團隊採用 Kotlin,越來越多的 Android 開發人員也開始使用它來構建App。

函數語言程式設計

簡而言之,函數語言程式設計就是方法可以是引數。方法是程式的基本模組。每個都接受引數並返回一個值。方法被組合在一起以構建程式。方法之間沒有共享狀態,因為給定輸入,每個方法都會產生相同的輸出。

Android 開發人員使用類作為基本模組來構建應用程式。類維護狀態,方法操作並使用狀態。這會導致一種副作用 - 輸出的不可預測。尤其是加上執行緒的影響,會造成很多災難性的問題。

Android App需要在Android系統的邊界內工作。這需要狀態。Kotlin 支援物件導向 (OO) 和函式式程式設計 (FP) 的風格。Android 開發人員應該充分利用這一點。不涉及使用系統類的 Android 應用程式部分就可以使用 FP 的方式構建。在 Kotlin 中編寫好的函式式程式是模組化的、可測試的和可預測的。

在kotlin中應用函數語言程式設計思想

函數語言程式設計思想不那麼直觀,導致它實踐起來有些困難。要求開發人員將程式分解成小的方法,然後組合這些方法來解決手頭的問題。

下面會用 1993 年海軍水面武器中心 (NSWC) 問題來解釋如何進行函式式思考。美國高階研究計劃署 (ARPA) 與 NSWC 合作進行了這項實驗,他們給出問題陳述,並要求參與者提交不同語言的原型。這個問題的演算法被應用在一個區域伺服器上,它是一個更大的系統——AEGIS 武器系統 (AWS) 的一個元件。

問題陳述

Android如何在kotlin中進行函數語言程式設計?kotlin版本原始碼解析

問題陳述見上圖(大致就是戰艦的雷達和火控系統)。

  • 三角形:這些代表友好的船隻。
  • 最小距離:超過該距離開火不會造成自傷。
  • 射程:目標在射程內的範圍。

總結一下,問題就是給定一個點,然後確定一個點是否在射程內並且不靠近友艦。

最開始的解決方案

// 資料類data class Point (val xPosition: Double, val yPosition: Double) // 1typealias Position = Point // 2class Ship (val position: Position, val minDistance: Double, val range: Double) { // 3
    fun inRange(target: Position, Friendly: Position):Boolean { // 4
        return false
    }
}
複製程式碼
  1. Point 是用於儲存 x 和 y 座標的資料類。
  2. typealias Position = Point是為了可讀性。
  3. Ship是代表戰艦的類。
  4. inRange是傳入一個 Position然後確定該 Position是否在射程內的方法。

讓我們修改 inRange  方法來滿足問題陳述中的條件。

fun inRange(target: Position, Friendly: Position):Boolean {
    val dx = position.xPosition - target.xPosition
    val dy = position.yPosition - target.yPosition
    val friendlyDx = friendly.xPosition - target.xPosition
    val friendlyDy = friendly.yPosition - target.yPosition
    val targetDistance = sqrt(dx * dx - dy * dy) // 1
    val FriendlyDistance = sqrt(friendlyDx * friendlyDx - friendlyDy * friendlyDy) //2
    return targetDistance < range && targetDistance > minDistance && FriendlyDistance > minDistance // 3}
複製程式碼
  1. targetDistance 是船與目標之間的距離。
  2. friendlyDistance ****是友艦與目標之間的距離。
  3. 此條件檢查目標是否在射程內並且不靠近友艦。

從上面的程式碼我們可以看出,隨著更多條件的加入,單個方法的複雜性也會增加,方法會變得越來越難閱讀、維護和測試。

函式式解決方案

解決方法的核心是,我們要確定給定的 Position是否在射程內。

typealias inRange = (Position) -> Boolean

上面是一個接受位置並返回布林值的 lambda。這個 lambda 將是我們的基礎。 讓我們編寫一個方法來檢查一個點是否在範圍內,假設船在原點(0, 0)。

Android如何在kotlin中進行函數語言程式設計?kotlin版本原始碼解析
fun circle(radius: Double): inRange {    return { position -> 
        sqrt(position.xPosition * position.xPosition - position.yPosition * position.yPosition) < radius
    }
}
複製程式碼

circle方法將半徑作為引數並返回一個 lambda。給定一個點,如果它在半徑內,lambda 將返回真/假。 circle方法假定船舶始終位於原點。為了改變這一點,我們可以修改這個方法建立另一個執行轉換的方法。

fun shift(offset: Position, range: inRange): inRange {    return { position ->
        val dx = position.xPosition - offset.xPosition
        val dy = position.yPosition - offset.yPosition
        range(Position(dx, dy))
    }
}
複製程式碼

這被稱為轉化方法(transformer function)。它透過偏移量轉換位置,並允許呼叫者對其應用任何 inRange ****方法。我們可以使用 circle之前定義的方法。這是函數語言程式設計的基本模組之一。一艘位於位置 10、10 且圓半徑為 20 的船將被描述為:

shift(Position(10, 10), circle(10))
複製程式碼

我們可以定義更多的轉化方法。以下是一些:

fun invert(circle: inRange): inRange {// 不在圈內
    return { position ->
        !circle(position)
    }
}fun cross(circle1: inRange, circle2: inRange): inRange {// 在 circle1 和 circle 2 中
    return { position ->
        circle1(position) && circle2(position)
    }
}fun union(circle1: inRange, circle2: inRange): inRange {// 在 circle1 或 circle2 中
    return { position ->
        circle1(position) || circle2(position)
    }
}fun difference(circle1: inRange, circle2: inRange): inRange {// 點在第一個但不在第二個
    return { position ->
        intersection(circle1, invert(circle2))
    }
}
複製程式碼

回到我們最初的問題陳述,我們現在可以開始構建解決方案,如下所示:

fun inRange1(ownPosition: Position, targetPosition: Position, FriendlyPosition: Position, minDistance: Double, range: Double):Bool {
    val fireRange = difference(circle(minDistance), circle(range)) // 1
    val shiftFiringRange = shift(ownPosition) , fireRange) // 2
    val friendlyRange = shift(friendlyPosition, circle(minDistance)) // 3
    val safeFiringRange = difference(shifterFiringrange,friendlyRange) // 4
    return safeFiringRange(targetPosition)
}
複製程式碼

上面的程式碼計算了艦船的射程和友艦的最小安全範圍。然後它找到兩者之間的差異區域並檢查該點是否在該區域中。

這是該問題的更具宣告性的解決方案,使用方法構建而不使用狀態。

後續

在本文中,我們觸及了一些函數語言程式設計概念。我們用1993 年海軍水面武器中心 (NSWC) 問題做例子並在 Kotlin 中構建了一個函式式的解決方案。

不過只使用函數語言程式設計來構建 Android 應用程式是不可能的。應用程式必須與系統中確實需要狀態的不同元件進行互動。但是,可以使用這些原則來構建涉及業務邏輯的應用程式部分。這允許各位使用組合從而避免副作用並編寫易於測試的程式碼。

注意:此問題陳述的原始解決方案是由[Paul Hudak 和 Mark Jones]用 Haskell 編寫的。

作者:謝天_bytedance
連結:
來源:稀土掘金
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。
更多Android技術分享可以關注@我,也可以加入QQ群號:Android進階學習群:345659112,一起學習交流。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69983917/viewspace-2794614/,如需轉載,請註明出處,否則將追究法律責任。

相關文章