Swift:无条件的可选

您好,我亲爱的开发人员,

在我分享了有关避免重复的无辜帖子之后,iflets和Guards难以理解,隐式的可选选项和强制转换的效果如何,我收到了来自Reddittors社区的大量反馈。 他们中的一些人甚至声称我无能,对其他人不屑一顾,强奸,并呼吁采取种族灭绝行动。 除了那些过分夸张之外,我注意到的是,大多数注释都标记为Optional作为条件语句中使用的实体或该实体,这会导致崩溃。 作为避免重复数据删除使用的语言功能的借口,我听到的另一重要立场是,代码将变得难以理解。

至少我感到有些困惑,因为条件是使用Optionals的最困难和冗长的方式。 这是因为我们在标准库中有大量的功能,这些功能专门用于可选处理。 我什至没有提到SwiftZ,SwiftX之类的工具,它们将本来就很强大的工具变成了真正令人惊奇的东西。 是的,如果您不了解其背后的基础知识,则它们是不可理解的,因为可选处理的方法源自数学。 但这不是避免使用它们的原因,而是学习新知识和改进的原因。

因此,让我们从头开始。 可选 (如果您不知道的话)是类型,它表示有无价值。 就如此容易。

现在,让我们看一下普通开发人员编写的一些常规Swift代码:

免责声明这是概述方法的示例代码,其他代码段也是如此。 请原谅我,我所有的读者,他们确实理解这一点。 我的文章描述的是通用方法,而不是特定的用例,您会为没有多少人感到震惊。

函数的作用很明显,但是我仍然会解释。 您将两个字符作为边界传递,并接收到两个字符之间的范围内的1个字符长的字符串数组。

我在这里写的唯一一本很少使用的是charToUInt ,因为我想避免重复。 charToUInt是用闭包方式编写的典型函数,因为在我看来,使用func声明如此小巧且易于掌握的函数有点过头了。 您可以在我的文章中了解有关这种重复数据删除的更多信息:

没人理会的常见错误– Swift中的一阶函数
您好,我亲爱的开发人员,让我们看一看完全不同的重复案例。 假设我们… blog.idapgroup.com

亲爱的读者,你们中的大多数人都已经编写或看到了这样的代码。 但是您是否曾经想过,您可以改进该代码,使其更具可扩展性,灵活性和短时性?

现在,在我们进行下一步之前,我强烈建议您阅读这篇有关Monad,Functor和Applicatives的出色文章。 尽管我不会在SwiftZ中充分利用本文的所有概念,但是您仍然会帮自己一个忙,因为您肯定会成为更好的SDE。 而且,它将简化阅读我的文章。

作为快速参考:

  • Monad实现func flatMap (f:(T)-> M )-> M ,其中M是Monad,包装一些值T
  • 函子实现func map (f:(T)-> T)-> M ,其中F是函子,包装一些值T。

因此,考虑到所有这些知识,让我们看一下如何将这些知识应用于conditionalCharacterStrings

如果您准备好要发誓,现在是关闭文章并写评论我多么可怕的最佳时机。

其余所有读者必须了解一件重要的事情。 这是解决问题的另一种方式。 它基于您最可能不习惯的概念(否则,您为什么要阅读该文章?)。 了解它们之后,您将一生了解它们,并且能够轻松阅读所有此类代码。 为什么? 由于mapflatMap是数学上的高阶函数,因此您可以在其中处理另一个函数并返回一个函数作为结果。 它们就像算术一样简单。 还记得,当你还是一个女学生/男孩时? 那时的乘法和除法也相当困难,但是现在您可以毫无问题地使用它们。 所以,不要害怕。

现在,让我们逐行找出到底发生了什么。

Bounds只是将上一个示例中的charToUInt应用到函数输入并解开过程中首次调用的Optional结果的方法。 为什么要拆开包装? 它的签名是: func flatMap (_转换:(Element)-> ElementOfResult?)-> [ElementOfResult] 。 这意味着,当作为参数传递的函数的结果为Optional时,数组中的flatMap会自动展开非nil值,并使用这些值返回新数组。

可能会发生,我们的输入在utf16中可能没有表示形式,因此如果在UInt16中无法转换其中一个或两个输入,则我们将返回一个空数组。

Array中的 func min也返回Optional 。 这是因为数组可能为空。 在这种情况下,其中将没有最小值。 因此,我只是将其解包为默认值。 由于之前已经执行过count条件检查,因此将永远不会使用默认值,因此这只是避免隐式展开的一种方法。

范围UInt16类型。 它是结果的数字表示,其中每个值对应一个字符。

现在介绍所有内容。 range.flatMap的行为与Array相同,当函数作为参数传递给Array时,返回OptionalUnicodeScalar($ 0)实际上返回了一个Optional ,所以return语句的第一行只是创建一个Array ,它包含范围内的所有数字,可以表示为UnicodeScalar

return语句的第二行通过将转换函数应用于Array 的每个元素,将Array 转换为Array <String >

对于该解决方案,我有几点不满意的地方:

  • 如果代码更改(超出范围的异常不是一个玩笑),则单独使用下标可能会导致崩溃。因此,有必要对所有情况进行广泛而详尽的测试;
  • 下标掩盖了代码的意图;
  • 抢先出口点中断了处理流程;
  • 默认值看起来不太好。

因此,为了解决这些问题,让我们改进一下方法:

func toTuple仅仅是一个hack,因为到目前为止 ,Swift中的元组确实非常有限。 他们不能遵守协议,他们不能拥有自己的功能。 在现实世界中,SwiftZ中用于Kinds and Curry的代码生成方法是首选。 可悲的是,这是到目前为止的唯一方法。 此函数仅返回Array的2个前一个元素的元组,如果Array的计数为2,则返回nil。

至于return语句,它与上一示例基本相同,但有一些怪癖:

  • 处理的结果是可选的,因此如果无法将输入转换为UInt16或边界中没有最小值,则默认为空结果。
  • bounds.toTuple()。map需要一个函数,该函数返回一个由map自身包装在Optional中的值。

如果您是第一次使用这种方法,那么该解决方案将难以理解。 另一方面,当您习惯这种方法时,它更具可读性,并且具有可扩展性和鲁棒性。 它清楚地表达了在数据处理方面的意图。 它避免了所有不必要的重复。 总体而言,解决方案是没有早期故障的处理链。 唯一不安全的代码被隔离在单独的函数中,该函数需要进行广泛而详尽的测试,然后可以在任何地方重复使用。 如果swiftc不太笨,无法在合理的时间内解决表达式,它甚至可以写成单个可链接的return语句。

因此,如果您冒险尝试使用此方法,基本上取决于您。 如果这样做,您会惊讶于代码变得如此简单和简短,处理,修改和提取出重复的情况如此简单。

就是这样,伙计们。 祝您有美好的一天,无论您身在何处,都要保持干燥。