import Foundation
// 定义两种类型: Distance 和 Position
typealias Distance = Double
struct Position {
var x: Double
var y: Double
var length: Double {
return sqrt(x*x + y*y)
}
}
extension Position {
// 用于检验一个点是否在图2.1中的灰色区域里
func inRange(range: Distance) -> Bool {
return sqrt(x*x + y*y) <= range
}
func minus(p: Position) -> Position {
return Position(x: x - p.x, y: y - p.y)
}
}
屏幕快照 2019-06-17 下午3.41.44.png
// 允许船有自己的位置,不一定在原点,引入一个结构体,它有一个属性为 position
struct Ship {
var postion: Position
var firingRange: Distance
var unsafeRange: Distance
}
屏幕快照 2019-06-17 下午3.46.37.png
屏幕快照 2019-06-17 下午3.46.48.png
extension Ship {
func canEngageShip(target: Ship, friend: Ship) -> Bool {
let dx = target.postion.x - postion.x
let dy = target.postion.y - postion.y
let targetDistance = sqrt(dx*dx + dy*dy)
let friendDx = friend.postion.x - target.postion.x
let friendDy = friend.postion.y - target.postion.y
let friendDistance = sqrt(friendDx*friendDx + friendDy*friendDy)
return targetDistance <= firingRange && targetDistance > unsafeRange && (friendDistance > unsafeRange)
// 目标在开火范围内 && 避免与过近的敌方交战 && 避免敌方过于接近友方
}
// 添加辅助函数之后代码更清晰易读
func canEngageShip1(target: Ship, friend: Ship) -> Bool {
let targetDistance = target.postion.minus(p: postion).length
let friendDistance = friend.postion.minus(p: target.postion).length
return targetDistance <= firingRange && targetDistance > unsafeRange && (friendDistance > unsafeRange)
}
}
使用 Region 类型
// Region 类型将指代把 Position 转化为 Bool 的函数
// 函数式编程的核心理念就是函数是值,它和结构体、整型或是布尔型没有什么区别
typealias Region = (Position) -> Bool
// summary: 使用一个能判断给定点是否在区域的函数来代表一个区域,而不是定义一个对象或结构来表示它
// 定义的第一个区域: 以原点为圆心的圆(circle)
func circle(radius: Distance) -> Region {
return { point in point.length <= radius }
}
// 定义的第二个区域: 圆心为任意点的圆 (circle2)
func circle2(radius: Distance, center: Position) -> Region {
return { point in point.minus(p: center).length <= radius }
}
// 区域变换函数,按一定的偏移量移动一个区域: 可以对更多的组件(圆/矩形等)作出同样的改变
func shift(region: @escaping Region, offset: Position) -> Region {
return { point in region(point.minus(p: offset)) }
}
// 一个圆心为 (5, 5) 半径为 10 的圆,可以用下面的方式表示:
shift(region: circle(radius: 10), offset: Position(x: 5, y: 5))
// summary: 为了避免创建像 circle2 这样越来越复杂的函数,我们编写了一个 shift(_:offset:) 函数来改变另一个函数
// 反转一个区域以定义另一个区域,这个新产生的区域由原区域以外的所有点组成:
func invert(region: @escaping Region) -> Region {
return { point in !region(point)}
}
// 计算参数中两个区域的交集
func intersection(region1: @escaping Region, _ region2: @escaping Region) -> Region {
return { point in region1(point) && region2(point)}
}
// 计算参数中两个区域的并集
func union(region1: @escaping Region, region2: @escaping Region) -> Region {
return { point in region1(point) || region2(point)}
}
// 所有在第一个区域中但不在第二个区域中的点构成的区域:
func difference(region1: @escaping Region, region2: @escaping Region) -> Region {
return intersection(region1: region1, invert(region: region2))
}
extension Ship {
// 将一些小型函数装配起来, 使用 Region 函数重构
func canEngageShip2(target: Ship, friend: Ship) -> Bool {
let safeRegion = difference(region1: circle(radius: firingRange), region2: circle(radius: unsafeRange))
let firingRegion = shift(region: safeRegion, offset: postion)
let friendRegion = shift(region: circle(radius: unsafeRange), offset: friend.postion)
let resultRegion = difference(region1: firingRegion, region2: friendRegion)
return resultRegion(target.postion)
}
}
完整代码
import Foundation
// 定义两种类型: Distance 和 Position
typealias Distance = Double
struct Position {
var x: Double
var y: Double
var length: Double {
return sqrt(x*x + y*y)
}
}
extension Position {
// 用于检验一个点是否在图2.1中的灰色区域里
func inRange(range: Distance) -> Bool {
return sqrt(x*x + y*y) <= range
}
func minus(p: Position) -> Position {
return Position(x: x - p.x, y: y - p.y)
}
}
// 允许船有自己的位置,不一定在原点,引入一个结构体,它有一个属性为 position
struct Ship {
var postion: Position
var firingRange: Distance
var unsafeRange: Distance
}
extension Ship {
func canEngageShip(target: Ship, friend: Ship) -> Bool {
let dx = target.postion.x - postion.x
let dy = target.postion.y - postion.y
let targetDistance = sqrt(dx*dx + dy*dy)
let friendDx = friend.postion.x - target.postion.x
let friendDy = friend.postion.y - target.postion.y
let friendDistance = sqrt(friendDx*friendDx + friendDy*friendDy)
return targetDistance <= firingRange && targetDistance > unsafeRange && (friendDistance > unsafeRange)
// 目标在开火范围内 && 避免与过近的敌方交战 && 避免敌方过于接近友方
}
// 添加辅助函数之后代码更清晰易读
func canEngageShip1(target: Ship, friend: Ship) -> Bool {
let targetDistance = target.postion.minus(p: postion).length
let friendDistance = friend.postion.minus(p: target.postion).length
return targetDistance <= firingRange && targetDistance > unsafeRange && (friendDistance > unsafeRange)
}
}
// Region 类型将指代把 Position 转化为 Bool 的函数
// 函数式编程的核心理念就是函数是值,它和结构体、整型或是布尔型没有什么区别
typealias Region = (Position) -> Bool
// summary: 使用一个能判断给定点是否在区域的函数来代表一个区域,而不是定义一个对象或结构来表示它
// 定义的第一个区域: 以原点为圆心的圆(circle)
func circle(radius: Distance) -> Region {
return { point in point.length <= radius }
}
// 定义的第二个区域: 圆心为任意点的圆 (circle2)
func circle2(radius: Distance, center: Position) -> Region {
return { point in point.minus(p: center).length <= radius }
}
// 区域变换函数,按一定的偏移量移动一个区域: 可以对更多的组件(圆/矩形等)作出同样的改变
func shift(region: @escaping Region, offset: Position) -> Region {
return { point in region(point.minus(p: offset)) }
}
// 一个圆心为 (5, 5) 半径为 10 的圆,可以用下面的方式表示:
shift(region: circle(radius: 10), offset: Position(x: 5, y: 5))
// summary: 为了避免创建像 circle2 这样越来越复杂的函数,我们编写了一个 shift(_:offset:) 函数来改变另一个函数
// 反转一个区域以定义另一个区域,这个新产生的区域由原区域以外的所有点组成:
func invert(region: @escaping Region) -> Region {
return { point in !region(point)}
}
// 计算参数中两个区域的交集
func intersection(region1: @escaping Region, _ region2: @escaping Region) -> Region {
return { point in region1(point) && region2(point)}
}
// 计算参数中两个区域的并集
func union(region1: @escaping Region, region2: @escaping Region) -> Region {
return { point in region1(point) || region2(point)}
}
// 所有在第一个区域中但不在第二个区域中的点构成的区域:
func difference(region1: @escaping Region, region2: @escaping Region) -> Region {
return intersection(region1: region1, invert(region: region2))
}
extension Ship {
// 将一些小型函数装配起来, 使用 Region 函数重构
func canEngageShip2(target: Ship, friend: Ship) -> Bool {
let safeRegion = difference(region1: circle(radius: firingRange), region2: circle(radius: unsafeRange))
let firingRegion = shift(region: safeRegion, offset: postion)
let friendRegion = shift(region: circle(radius: unsafeRange), offset: friend.postion)
let resultRegion = difference(region1: firingRegion, region2: friendRegion)
return resultRegion(target.postion)
}
}
// ___
// //|||\\
// //|-_-|\\
// |||
// |
// |
// / \
网友评论