Swift40/90Days - 用函数式编程解决逻辑难题
逻辑难题前阵子朋友和我说起,把1美元分解成更小的面额,有293种方法。换句话说,如果一个哥们儿告诉你他有1美元,那么他的手里有293种可能的组合,有可能是两个50美分,也可能是4个25美分。第二天,我就开始尝试用代码去解决这个问题。这篇博客回顾了当时想到的两种解决方案。 美元硬币对于不熟悉美元硬币的同学,可以先了解一下美元的硬币。如下图所示,1美元(dollar) = 100美分(cent):
初探问题思考后我发现用一种比较简单肮脏的手段解决这个问题并不难,但是这还远远不够。我想找到一种优雅的解决方案,所以我尝试从各个角度思考这个问题,最终得到了想要的答案。 解决这个问题的关键在于递归的分解问题。“如何用各种硬币组合拼成1美元”,更宽泛点讲,其实就是“如何用各种硬币组合拼成指定金额”。 举个人民币的例子。你欠人家100块,人家说你100块都不给我。你说好,我给!于是掏出两张50,这便是一个50+50的解决方案。 点击 这里 查看完整的算法回顾。 先造硬币我多次提到“硬币”这个词,实际上一枚硬币也就是一个整数值,代替了它价值多少美分。我写一个枚举类存储所有的硬币面额,然后再用一个静态方法降序返回所有的值: enum Coin: Int { case SilverDollar = 100 case HalfDollar = 50 case Quarter = 25 case Dime = 10 case Nickel = 5 case Penny = 1 static func coinsInDescendingOrder() -> [Coin] { return [ Coin.SilverDollar,Coin.HalfDollar,Coin.Quarter,Coin.Dime,Coin.Nickel,Coin.Penny,] } } 解决方案1:指令式编程 - Imperative指令式编程的一个重要观点是:变量改变状态。指令式的程序像是一种微型控制器,它告诉计算机如何完成任务。接下来的 Swift 代码大家看起来应该都不陌生,因为 objc 就是一种指令式的编程语言: func countWaysToBreakAmout(amount: Int,usingCoins coins:[Coin]) -> Int{ let coin = coins[0] if (coin == .Penny) { return 1 } var smallerCoins = [Coin]() for index in 1..<coins.count { smallerCoins.append(coins[index]) } var sum = 0 for coinCount in 0...(amount/coin.rawValue) { let remainingAmount = amount - (coin.rawValue * coinCount) sum += countWaysToBreakAmout(remainingAmount,usingCoins: smallerCoins) } return sum } 仔细看下上面的代码,计算过程一共分三步:
这样的代码对于指令式编程来说再平常不过,接下来我们就来看下如何用函数式编程解决这个问题。 解决方案2:函数式编程 - Functional函数式编程的依赖对象,是函数,而不是状态变化。没有太多的共享数据,就意味着发生错误的可能性更小,需要同步数据的次数也越少。 Swift 中函数已经是一等公民,这让高阶函数变成可能,也就是说,一个函数可以是通过其它函数组装构成的。随着 objc 中 block 的引入, iOS 开发者对这个应该并不陌生。 下面是我的函数式解决方案: func countWaysToBreakAmount(amount: Int,usingCoins coins:Slice<Coin>) -> Int{ let (coin,smallerCoins) = (coins[0],coins[1..<coins.count]) if (coin == .Penny) { return 1 } let coinCounts = [Int](0...amount/coin.rawValue) return coinCounts.reduce(0) { (sum,coinCount) in let remainingAmount = amount - (coin.rawValue * coinCount) return sum + self.countWaysToBreakAmount(remainingAmount,usingCoins: smallerCoins) } } 第二个参数是 我用元组的语法同时获取了 接下来,也并没有采用循环然后改变局部变量的方法来计算剩余的组合数,而是用 首先 思考Swift 对于函数式编程的支持让我感觉的兴奋,Excited!换种方式思考或许是个不小的挑战,但是这都是值得的。几年前我自学了一些 Haskell ,我很欣喜的发现一些函数式思考习惯,让我在 iOS 开发中也能受益匪浅。 示例项目的源代码可以在这里下载。 原文地址:
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |