计算特定字符在字符串中出现的次数

问题:随便给定一个字符串儿,如何计算一个特定字符在该串儿中出现的次数?

  比如说,我现在给定这样一段字符串儿:“The US State Department on Friday has officially delivered notice to the UN of its intentions to withdraw from the 2015 Paris climate pact.”,你能告诉我它里面有多少个字符i吗?

计算给定字符串儿中特定字符出现的次数.png

  解决这个问题也不是太难,最常规的做法就是使用for...in循环进行遍历,找出所有的特定字符,然后再将结果进行返回:

// 常规的解决方案
func challenge(input: String, character: Character) -> Int {
    
    // 定义一个变量,用来记录特定字符的数量
    var letterCount = 0
    
    // 遍历字符串儿中所有的字符
    for letter in input.characters {
        
        // 如果发现特定字符
        if letter == character {
            
            // 数量加1
            letterCount += 1
        }
    }
    
    // 将计算结果返回
    return letterCount
}


let str = "The US State Department on Friday has officially delivered notice to the UN of its intentions to withdraw from the 2015 Paris climate pact."
challenge(input: str, character: "i")  // 结果为11个

  上述问题完美解决。当然,如果仅仅是这样,那就太没意思了!接下来就厉害了,因为我要开始装逼了,而且还是三连发:

// 装逼第一弹
func challenge(input: String, character: Character) -> Int {
    
    // 返回要查找的字符character的数量
    return input.characters.reduce(0) {
        
        // 如果参数$1和要查找的字符character相等,则返回$0加1;否则就直接返回$0
        $1 == character ? $0 + 1 : $0
    }
}

let str = "The US State Department on Friday has officially delivered notice to the UN of its intentions to withdraw from the 2015 Paris climate pact."
challenge(input: str, character: "i")  // 结果为11个

  这段代码是不是比使用for...in循环要简洁许多?但是,简洁的代码一般都不太容易理解,所以接下来我需要对上面的代码做进一步的解释。要想完全理解上面的代码,首先必须知道三个知识点:第一个是reduce(_:_:)函数;第二个则是尾随闭包;第三个则是省略闭包参数

  我们先来解释reduce(_:_:)函数。它接收两个参数,第一个参数initialResult作为初始值,会在nextPartialResult这个闭包首次执行时传递给它;而第二个参数,也就是nextPartialResult这个闭包,其作用是将累加值和序列中的元素组合成一个新的累加值,并且在nextPartialResult闭包再次调用时,将这个新的累加值返回给调用者。reduce(_:_:)函数的返回值就是最终累加的值。当然,如果序列中没有任何元素,那么它的返回值就是initialResult。

  第二个需要了解的概念是尾随闭包。所谓的尾随闭包,就是指当一个函数接收多个参数,并且最后一个参数是一个闭包时,可以将这个闭包参数写在函数小括号后面。详细解释参见我的另一篇笔记《Swift中的闭包》。搞清楚上面这个两个概念之后,为了便于理解,我们可以把上面的代码完整的写出来:

func challenge(input: String, character: Character) -> Int {
    return input.characters.reduce(0, { (num: Int, char: Character) -> Int in
        char == character ? num + 1 : num
    })
}

  因为我们要计算字符串儿str中所有字符i的数量,所以最开始计数时其个数应该为0,这也就是为什么reduce(_:_:)函数的第一个参数值为0的原因。接下来,我们详细解释一下reduce(_:_:)函数的第二个参数,也就是好大一坨的那个闭包。闭包第一个参数num表示特定字符char的个数,第二个参数char表示要计算的特定字符。当char和character相等时(也就是找到特定的字符),我们就让num的值加1,否则就直接返回num的值。

  最后再来说一下省略闭包参数。一般情况下,如果闭包中有多个参数,我们可以使用$0、$1、$2、... 、$n来表示这些参数,其中$0就表示闭包中的第一个参数,而$1就表示闭包中的第二个参数...依次类推。因此,上面的代码可以表示为:

func challenge(input: String, character: Character) -> Int {
    
    return input.characters.reduce(0, {$1 == character ? $0 + 1 : $0})
}

  如果再结合尾随闭包的知识,那么它就可以写成我们最开始的样子了。不过,需要说明的是,使用reduce(_:_:)函数倒是书写方便了,不过性能上要比直接使用for...in循环差一些。正所谓装逼是有代价的。

  一般情况下,装完逼就应该赶紧跑,但是我岂是那样的人?我得留下来继续装逼。所以,装逼第二弹来了:

func challenge(input: String, character: String) -> Int {

    // 将输入的每个字符转换成字符串儿,并且创建字符串儿数组
    let array = input.characters.map { String($0) }

    // 用字符串儿数组array创建一个可计数集合
    let counted = NSCountedSet(array: array)

    // 计算character重复出现的次数,并且将结果返回
    return counted.count(for: character)
}

let str = "The US State Department on Friday has officially delivered notice to the UN of its intentions to withdraw from the 2015 Paris climate pact."
challenge(input: str, character: "i")  // 结果为11个

  上面这段代码理解起来比使用reduce(_:_:)函数那个要简单一些。主要知识点就在于map(_:)函数和NSCountedSet这个类。map(_:)函数的参数transform是一个映射闭包, 而这个闭包又接收来自序列中的元素作为其参数,闭包对参数进行某种变换之后,再将其作为相同类型或者不同类型的变换值返回出去。整个map(_:)函数的返回值是一个包含此序列的转换元素的数组。就以我们上面的代码而言,我们将字符串儿str中的第一个字符强制转换成String类型(未转换之前,str中单个的字符是Character类型),那么最后的结果就是str中所有的字符都会被转换成String类型,并且存放在一个[String]类型的数组中。NSCountedSet的作用是将[String]类型的数组array变成一个可计数的集合,而这个集合中有一个count(for: )方法,它可以计算出一个可计数的集合中有多少个重复的指定字符。为了便于理解,我还是将map(_:)函数部分完整的写出来:

func challenge(input: String, character: String) -> Int {

    // 将输入的每个字符转换成字符串儿,并且创建字符串儿数组
    let array = input.characters.map { (char: Character) -> String in
        
        // 将字符char从Character类型强制转换成String类型,并且返回
        return String(char)
    }

    // 用字符串儿数组array创建一个可计数集合
    let counted = NSCountedSet(array: array)

    // 计算character重复出现的次数,并且将结果返回
    return counted.count(for: character)
}

  需要说明的是,使用map(_:)函数这种解决方案,其性能比使用reduce(_:_:)函数那个还要差(这个可以通过Playground进行查看),所以,这个也是拿来装逼用的。

  我们搞了那么久,难道就是为了装逼吗?有没有既简洁,又性能不错的解决方案呢?当然没有了!惊不惊喜?意不意外?…… 其实还是有的,我们可以考虑把字符串儿str里面所有包含指定的字符character都删掉,然后再将新字符串儿的长度和原始字符串儿str的长度进行比较,其结果就是我们想要的答案:

func challenge(input: String, character: String) -> Int {

    // 将字符串儿中所有的character字符替换成空的,并且返回一个新的字符串儿
    let modified = input.replacingOccurrences(of: character, with:"")

    // 原始字符串儿的个数再减去新字符串儿的个数,其结果就是所有character字符的个数
    return input.characters.count - modified.characters.count
}

let str = "The US State Department on Friday has officially delivered notice to the UN of its intentions to withdraw from the 2015 Paris climate pact."
challenge(input: str, character: "i")  // 结果为11个

  最后主要是用到了字符串儿的replacingOccurrences(of: with: )方法,它可以用来替换指定的子字符串儿,并且返回一个全新的字符串儿。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354

推荐阅读更多精彩内容

  • 第2章 基本语法 2.1 概述 基本句法和变量 语句 JavaScript程序的执行单位为行(line),也就是一...
    悟名先生阅读 4,149评论 0 13
  • 前言 最先接触编程的知识是在大学里面,大学里面学了一些基础的知识,c语言,java语言,单片机的汇编语言等;大学毕...
    oceanfive阅读 3,077评论 0 7
  • 第3章 基本概念 3.1 语法 3.2 关键字和保留字 3.3 变量 3.4 数据类型 5种简单数据类型:Unde...
    RickCole阅读 5,126评论 0 21
  • 最近一直没有更新,不是我忘了,而是,有一种无法言语的情绪左右了自己,陷入了与自己的较真中。我在打磨自己的表达,想把...
    洛雨芳霏阅读 151评论 0 0
  • 其实,每个人每天所做的事情都是在重复着一天又一天。 为什么这么说呢? 生活,包含着太多的意义了。 对于初出社会的青...
    水舞月间阅读 219评论 1 1