Tag: 函数式编程

Swift中的函数式编程

如果我们查找有关函数式编程的文章,则其中大多数都在谈论高级函数式编程,例如副作用,无状态,不变性或monad之类的内容 ,而其他基本示例(例如map的工作方式)则太简单了,但是在这里试图通过使用Swift中的示例来展示函数式编程的实用方面。 这篇文章的第一部分将为您简要概述函数式编程和基本定义。 示例部分显示了如何以功能样式编写命令性非功能代码。 高阶函数 函数式编程语言的第一个显着区别是,可以将函数分配给值并传递给其他函数。 这就是为什么他们被称为头等公民。 当您说一种语言具有一流的功能时,这意味着它将一种功能视为值,您可以将一个功能分配给一个变量,或者将其传递。 一流的功能被视为一个对象。 有一个更高层次的功能。 它们接受一个或多个函数作为参数或返回其他函数。 高阶函数是可以在其他函数上使用的函数,即它们将函数作为参数,也可以返回一个函数。 映射,过滤,缩小 Map是著名的高阶函数之一。 Map在数组(更确切地说是集合)上工作,它接受一个函数(称为transform)作为参数,并将此transform函数应用于每个元素,并返回结果数组,作为“ transformed”元素的新数组。 使用map有一些优点:它更短并且需要更少的代码 , 更少的错误空间 ,更重要的是它更干净 。 假设我们有一个字符串数组,要计算每个字符,我们必须1.遍历该数组,2.获取每个元素的计数,3.将其追加到新数组中: let houses = [“Stark”, ” Baratheon “, ” Targaryen “, “L annister “] var letterCounts:[Int] = [] 用于房屋{ letterCounts.append(item.count) } print(letterCounts) // [5, 9, 9, 9] 5,9,9,9 // [5, 9, 9, 9] 显然,功能性方法更为简洁: […]

转向漂亮的异步Swift代码

本文提高了对与异步代码相关的问题的认识,并提供了在Swift 3.0–3.1上进行编程时解决这些问题的示例。 样本问题的描述 这是源数据: Person是包含有关人员信息的结构的示例。 MyService是用作模型入口点的类的示例。 MyViewController是管理与UI相关的实例的类的示例。 MyService必须将Person提供给MyViewController以返回具有相应标识符的请求。 它可能在内存中没有所请求的信息,因此获取人员数据可能涉及网络,磁盘操作等。 回到同步编码时代 我注意到许多项目仍然使用同步方法。 因此,让我们首先使用它来解决示例问题。 扩展MyService { func person(identifier:String)throws-> Person? { 返回/ *从网络获取人员* / } } 似乎非常简单: input arguments -> output result 。 此方法可以返回该人(如果没有这样的人,则为nil);如果出了问题,则抛出一个问题。 它的使用方式就是这样: 扩展MyViewController { func present(personWithID标识符:字符串){ / *不要忘记调度到后台队列* / DispatchQueue.global()。async { 做{ 让人=尝试self.myService .person(标识符:标识符) / *不要忘记调度到主队列* / DispatchQueue.main.async { self.present(人:人) } } { / *不要忘记调度到主队列* […]

在Swift中进行功能性思考

如上图所示,从服务器解析JSON响应需要进行两次转换: 从原始HTTP响应转换为数据的JSON表示形式。 从JSON表示形式到模型。 在第一步和第二步之间,我们将使用数据的JSON表示形式。 这种JSON表示形式使我们能够对服务器响应进行增量转换。 通过在模型之前解析为中间JSON表示,我们的代码变得更加可组合。 JSON的表示方式对于理解本文中的功能编程概念并不是必不可少的。 但是,为了完整起见,让我们现在对其进行定义。 如果阅读JSONSerialization的文档,您会注意到有效JSON的规则之一是: 所有对象都是NSString , NSNumber , NSArray , NSDictionary或NSNull 。 由于案例数量有限,因此使用枚举似乎是一个好情况。 定义了JSONObject类型后,我们现在知道第一次转换之前,第一次与第二次转换之间以及第二次转换之后的数据类型。 它们是Data -> JSONObject > Model (我们稍后会定义模型。) 功能方法 有一个核心概念将通过此练习来推动我们的思考过程。 我们将过程中的每个步骤都视为独立于函数外部变量的转换 。 让我们看一下如何将这种思考过程应用于上面概述的两个转换。 转换1:将数据转换为JSON对象 我们将从定义一个新类型Deserialize开始,该类型将Data转换为JSONObject 。 在函数式编程中,类型由其方法签名定义。 要使用此类型,我们编写了一个函数,该函数返回新的Deserialize类型。 我想强调关于JSON()函数的几件事。 该函数返回一个闭包。 以面向对象的思维方式看待这个问题可能看起来很奇怪,但这是标准的函数式编程。 请注意,从JSON()返回的闭包完全独立于该函数外部可能存在的任何状态。 这与我们非常依赖状态的面向对象编程形成了鲜明的对比。 转换2:将JSON对象转换为模型 正如我们在第一个转换中所做的那样,让我们​​定义一个新类型,该类型接受一个JSONObject作为参数并返回类型T的模型: typealias Decode =(JSONObject?)->(T?) 注意:我们使 Decode 函数通用,因此我们可以解码各种模型。 由于我们尚未定义任何模型,因此这是我们开始从JSON创建模型所需的所有样板代码! 下一步是定义我们的模型以及可以解析JSON的函数。 全部放在一起 让我们导出一个简单的示例,以显示与该代码交互的外观。 对于此示例,我们将期望服务器向我们发送有关用户的信息。 我们将定义User模型,并编写一个函数decodeUser() ,以执行从JSONObject到User的转换。 […]

使用Swift探索存在类型

作为程序员,我们花费大量时间编写程序来解决问题,自动化任务,收集数据,分析数据以及制定希望使我们的用户生活更加轻松的决策。 我们的大多数白天编程本质上都是实用的,因为世界是建立在截止日期和预算以及用户故事和冲刺之上的。 只有当孩子们开始入睡并且白天的烦恼逐渐消失在背景中时,我们才能编写夜间程序,探索程序,学习程序,存在性程序。 我因此编程! 不,这不是一个存在的类型,只是一个me脚的笑话。 临时多态性 在这样一个探索的夜晚,我想尝试Swift的即席多态性。 临时多态性听起来很有趣,但是每所小学都理解并使用它。 1 +1 = 2 1.0 + 1.0 = 2.0 加法运算符使我们可以将整数相加,将浮点数相加,将玩具熊甚至是Elsa娃娃相加。 小学孩子们知道加法不限于事物的一个特定子集。 当然,编程语言从来都不是那么直观的,通常来说,我们使用的语言很少支持即席多态。 例如,在OCaml中,我们有一个不同的运算符来添加整数和浮点数。 1 +1 = 2 1.0 +。 1.0 = 2.0 Java和.Net都不是更好,因为它们都不允许我们将相同的函数应用于不同的类型。 但是,Swift的精神是使专业人员和学龄儿童都可以访问编程,并且由于学童们了解临时的多态性,也许职业程序员也应该这样做。 协议编号{ 关联类型A 静态函数Add(x:A,y:A)-> A } 扩展名Int:Num { 静态函数Add(x:Int,y:Int)-> Int {return x + y} } 扩展名Double:Num { 静态函数Add(x:Double,y:Double)-> Double {return x + y} } […]

Swift —地图和flatMap

关于map&flatMap把网路上的一些资源做重点整理 参考: 斯威夫特烧脑体操:http://www.infoq.com/cn/articles/swift-brain-gym-map-and-flatmap 应用范例:http://blog.xebia.com/the-power-of-map-and-flatmap-of-swift-optionals/ Monad&Functor图解:http://www.ruanyifeng.com/blog/2015/07/monad.html Array有一种map函数,两个flatMap函数: 当map的闭包返回值,不是序列类型时,和flatMap的差异就只是在于是否判断结果为nil。 可选有一种地图函数,一种平面地图函数: 两者本质上一样,提供调用者闭包函数,可返回Optionalvalue,也可以是非Optionalvalue。 Array和Optional的map函数都叫一样的名字,是因为他们都是Functor 。 Array和Optional的flatMap函数都叫一样的名字,是因为他们都是Monad 。

功能视图构建

在情节提要中还是在代码中创建视图? 作为iOS开发人员,我们非常清楚这个问题。 他们两个都有优点和缺点,但是最近我越来越喜欢用代码创建的视图。 什么是架构? 我曾经将MVVM与RxSwift一起使用,这基本上意味着控制器是通过结合Storyboard , ViewController和ViewModel来创建的。 如果我们删除Storyboard界面,则在哪里构建视图的正确位置? 让我介绍一下ViewBuilders。 考虑一下当我们构建一个称为HomeViewController控制器时的情况。 让我们创建一个名为HomeViewBuilder的帮助程序结构,该结构最终返回HomeView ,该抽象使我们能够访问组件(等效于插座)。 因此,我们最终得到以下架构: HomeViewBuilder的结构: 也许您已经猜到我们要通过管道化一些操作(例如添加/设置适当的组件)来构建此视图。 让我们定义一个简单的ViewBuilder协议: 我们还定义实现ViewBuilder HomeViewBuilder 。 当然,最重要的部分是buildView函数。 我故意向您展示了此方法的最终版本 。 这是唯一可以从外部访问的公共方法,这是一种梦想,我们现在要实现。 首先 ,让我们介绍流行的管道和函数组合运算符,这些运算符允许我们组合函数和对象: 管道运算符用于buildView函数中,其基本外观如下: 组成从类型Builder功能到Builder 将此转换应用于创建的构建器 好的,看起来不错,但是这些 install / setup 功能 如何 工作? 如果您对镜头了解不多,则一定要观看一些视频,了解这种模式的工作原理。 我在这里使用它们来编写单独的小段代码,并使它们可重用。 让我们考虑使用vertical轴创建UIStackView并将translatesAutoresizingMaskIntoConstraints标志设置为false 。 我们将在项目中使用多少个? 当然很多 我创建了一些项目全局的Style结构,该结构定义了常用的样式,这些样式是(View) -> View类型的转换函数。 这里有些例子: 因此,让我们构建使用其中一种样式的UIStackView 。 这是典型installer功能的实现: 多亏了镜头组成,它看起来很清晰,我们在应用程序中获得了很多可重用性。 但是,等等,我们错过了重要的约束设置…… 问题 通过使用锚,我们可以很容易地通过将一个锚连接到另一个锚来生成约束。 但是在这里,我们失去了像镜头所使用的流程那样通用和通用的功能。 解决方案:让我们定义另一个构建器。 我创建了一个名为FunctionalBuilders的库,该库包装了约束构建并使其使用起来更加美观。 […]

功能性Swift-咖喱

在使用Swift时,我在函数式编程环境中进行了越来越深入的研究。 在其他编程语言(例如Python或Haskell)中使用的许多技术可以迅速获得。 今天,我想向您介绍其中一种,称为currying。 Swift是一种出色的编程语言,它利用了函数式编程的优势。 函数式编程的主要好处之一是函数是高阶公民,这意味着迅速使用函数可以执行以下操作之一: 将一个或多个函数作为参数 返回一个函数作为参数 最近,我在函数程序员Curry中发现了一种众所周知的技术。 Currying用于将具有多个参数的函数分解为一系列包含参数的函数。 当您想提高代码的可读性和可重用性时,此技术变得非常有用。 该函数如下所示: 如您所见,咖喱是一个简单的功能。 它以一个函数作为参数( fn ) 首先返回一个函数 ,该函数采用传入参数的函数的第一个参数传递给curry(此处为A ): (a: A) -> (B) -> C 然后,它返回一个函数 ,该函数采用传递给curry的函数的第二个参数(此处为B ): (b: B) -> C 最后,使用参数A和B调用作为参数传递的函数 : fn (a,b) 值得注意的是,函数fn仅在最后一个参数传递给curried方法时才计算 。 在某些情况下,此方法可能有用,其中一种情况是您希望使现有方法更方便或可重复使用: 让我们采用以下日志方法: 使用两个参数调用它可能变得很乏味,使用curry我们可以使它可重用且更明确,而无需创建一个封装参数的新函数。 充分利用高阶函数的优势,我们可以分解logMessage并创建一个变量,该变量是一个将日志消息作为参数的函数,然后将其记录在调试级别。 另一个用途是利用诸如map , forEach等功能性方法。 让我们来看下面的例子: 如您所见,使用currying可以很容易地部分应用任何方法。 (Vincent Pradeilles在我当时所在的CocoaHeads展示了这种用咖喱粉的用例。) 如果您正在使用RxSwift或计划使用它,您可能想知道如何将使用completionBlock的异步代码迁移到一系列Observable。 重构所有代码库只是为了使其可观察而不是(completionBlock /成功和失败块)通常是不可行的。 使用curry和扩展,您几乎不需要代码即可实现。 Rx + FromAsync […]

在Swift中使用镜头进行嵌套依赖项修改

我个人喜欢KrzysztofZabłocki在他的文章中描述的模式。 这是一种简单的纯解决方案,它在层次结构中构建了依赖关系。 让我们来看一个例子: 想象一下这样一种情况,我们有提供用户年龄并具有Validator对象作为依赖项的UserProvider 。 在ViewModel某个地方,我们有一个反应性信号,通知用户年龄是否合适,例如: 现在,在测试中,我们有一个默认的验证实现,如果用户年满18岁,则返回true。 但是出于某种原因(例如测试),我们希望修改此验证以始终返回false ,这意味着由于年龄原因,可能不允许用户查看内容。 请注意, Validator对象嵌套在UserProvider中, UserProvider也嵌套在整个AppDependencies对象中。 这意味着我们必须编写很多代码,例如: 这也意味着您必须使每个struct属性var看起来很奇怪,因为我们不应该突变那些依赖结构。 现在想象一下, UserValidator比这三个闭包复杂得多,并且包含的​​内容更多。 这意味着很多样板代码,这当然是不需要的,尤其是在应该快速进行的测试中。 解? 镜片。 斯威夫特镜头是布兰登威廉姆斯推出的结构。 您可以找到许多文章和视频来描述确切的镜头,但基本上,这些结构允许通过返回新实例来view并将某些特定属性set为不可变类型。 看到与UserValidator相同的修改,但是这次我们将使用镜头: 现在,我们保存与UserValidator执行的验证一样多的代码行。 但这是唯一的收获吗? 更改两个特定的(可能不同的)依赖项属性怎么样? 这就是镜头真正闪耀的地方! 请注意,我们修改了两个不同的属性,一个来自UserProvider ,另一个来自UserValidator. 这意味着您的应用程序中不再有任何样板代码,对于这些结构,您可以随意使用let而不是var🙂 但是等等,我错过了什么吗? 那些镜片在哪里? 我强烈建议您使用Sourcery工具(KrzysztofZabłocki的另一个工具)来自动生成镜头(并构造初始化器)。 您可以在Sourcery github存储库中找到模板。 通过将透镜与Sourcery结合使用,定义新的依赖关系并不重要,您只需要重新运行Sourcery脚本,一切都会自动更新。 这个故事与依赖注入的优缺点无关,但是它可能鼓励您在应用程序中使用此模式而不会受到伤害。 它使测试易于执行,并且确保快速进行。 从代码构造视图时,使用镜头也非常有用,但这是另一篇即将发表的文章的主题。 随时鼓掌! 😎

在Swift😇中给Zip()一个机会

Zip()可以节省您的时间,只需在代码中为其留出空间即可。 创建由两个基础序列构成的对对序列。 您可能不理解这些词的意思,那是我第一次读到的词,但是通过练习和一些搜索,可以用简单的词来定义它并显示它的作用。 zip(_:_:)函数旨在将两个序列合并为一个元组序列 有例子可以使事情变得容易: 让 sequence1 = [“” ali“,” ahmed“,”穆罕默德“] 让 sequence2 = [1,2,3] 让 mergedSequence_zip = zip(sequence1,sequence2) 打印(数组(mergedSequence_zip)) // [(“ ali”,1),(“ ahmed”,2),(“ mohamed”,3)] 看这个简单的例子知道会发生什么。 zip()将sequence1的第一项与sequ​​ence2的第一项合并/合并,并将这两项作为元组,对于第一项,然后对第二项和第三项进行相同的步骤 有zip的注释,zip()是否仅适用于Array? –没有 Zip()允许您使用Arrays,Sets,Dictionary或任何符合sequenceType协议的类型 因此,我们来看zip()对Dictionary类型的作用示例: let sequenceDic1 = [“名称”:“ ahmed”,“地址”:“伦敦”] let sequenceDic2 = [“名称”:“穆罕默德”,“地址”:“法国”] 让 zipDics = zip(sequenceDic1,sequenceDic2) 打印(数组(zipDics)) // [(((key:“ name”,value:“ ahmed”),(key:“ name”,value:“ Mohamed”)),(((key:“ address”,value:“ London”),(key:“地址”,值:“法国”))] 这次发生了什么,这是元组类型的数组,为简单起见,让我们访问第一个元素。 打印(Array(zipDics).first!) […]

Dyno版本:AWS,Swifter

上一次,我们做了很多设置以开始使用Amazon Web Services的DynamoDB ,包括使用Swift-Python桥,以便我们可以使用官方的AWS接口boto3与DynamoDB进行通信。 但是boto3除了基于Python并因此没有Swift类型安全性之外,还具有一些局限性:它很复杂,难以使用—并且存在同步的主要问题。 要查看该问题,请运行上次到达的代码: …现在,请尝试关闭计算机的WiFi ,然后重新运行。 怎么了? table.scan()行仅挂起那里30秒钟,直到出现令人讨厌的异常并且程序崩溃(带有不可恢复的致命错误)。 实际上,这不是我们希望库调用或可能具有间歇性网络连接的应用程序(例如移动应用程序¹)所期望的行为。 本文-更好的boto Dyno库旨在做得更好。在本文中,我们将介绍如何做! 和以前一样,尽管这个想法是产生一个有用的库,但我也希望展示可以在自己的代码中使用的技术。 我们将使boto3调用异步。 这将演示信号量,工作队列和工作项的使用 Dyno将利用新的Swift 5 Result类型发布一个Observable结果流。 在这里,我们将演示带有复杂数据流的Observable和Reactive编程 我们将添加一些有用的,类型安全的方法来从DynamoDb(原生于Swift)读取和写入数据。 这说明了 我们数据类型上的 一些出色的功能构造,例如 zip 和 flatMap 在本文的最后,我们将直接从Swift将Dinosaurs(当然是🦕和))写到DynamoDB数据库中,然后以异步方式读回它们,并适当考虑网络延迟。 这将成为我们Dyno库的开始。 和以前一样,该库正在公开开发中,因此您可以在github( swiftify分支)上查看源代码。 有很多事情要做,让我们开始吧! 可观察的流 正如我在上一篇文章中提到的,可观察对象是表示数据流的一种方式。 我们可以将它们连接到Reactive组件,以便能够以功能强大和声明性的方式处理数据流。 这是表示数据操作的一种非常强大的方法-我们将在以后的文章中介绍,但是现在我们将看看如何将DynamoDB交互表示为Observable。 建模数据交互的关键是要注意它们都看起来像这样: 要求DynamoDB做某事(扫描表,更新行等) 等待结果(返回200行,更新成功)… 或出现错误,例如 超时或数据完整性错误。 我们使用DynoActivity数据类型的可观察流对这些阶段进行DynoActivity : 它就像一串可观察的事物(橙色和红色的大理石代表可观察的事件)看起来像这样: 现在,我们要做的一件事是假设即使一次大型查询(例如,返回了数百行),我们也一次性获得了所有数据:我们不对输出进行“分页”。 我们将来可能会改变它²。 您可能还会注意到,我们希望我们的可观察流以多线程的方式异步工作:我们可以让多个流同时运行,有的读取数据,有的写入。 为什么我们不将Future用于这种类型的异步数据请求/响应? 使用Observable流可以非常轻松地处理诸如“显示等待图标,直到返回数据或显示错误”之类的交互模式。 对于实际应用而言,这是非常基本的。 现实检查 在创建高级Observable之前,我们需要处理以下事实:通过不可靠的连接与远程数据库进行同步接口,并使用Python接口进行引导。 具体来说,我们需要确保Dyno正在控制AWS连接上的活动,而不是将其留给Boto3的30秒同步,程序终止超时。 那么,如何在不自行控制Boto3代码的情况下使Boto3异步和多线程呢? 我们将使用DispatchSemaphores […]