读《Go并发编程实战》第4章 流程控制方式

一个网站的用户管理中,一般用户都会起一个昵称,当然用户A和用户B的昵称有可能相同,我们就使用类型为map[string]int的集合来表示,其中string代表用户昵称、int表示昵称相同的人数。

集合数据内容为:{"chen":20,   "chen长":10,   "春风":5,   "龙ge":13,   "where你are":7}。

§问题1. 从这个集合中找出只包含中文的用户昵称的计数信息

上面集合中只有用户昵称为“春风”的元素满足条件,其它像“龙ge”、“chen长”、“where你are”都不是只包含中文。像看程序实现:

users := map[string]int{"chen": 20, "chen长": 10, "春风": 5, "龙ge": 13, "where你are": 7}
var targetCount map[string]int = make(map[string]int)

for name, count := range users {
    matched := true
    for _, v := range name {
        if v < ‘\u4e00‘ || v > ‘\u9fbf‘ {
            matched = false
            break
        }
    }
    if matched {
        targetCount[name] = count
    }
}
fmt.Println(targetCount)

解释一下:

第一层for循环遍历users集合,得到昵称(name)和计数(count ):

for name, count := range users{

// ......

}

第二层循环遍历昵称name字符串中的每个字符,并判断是否有非中文字符:

for _, v := range name{

if v <‘\u4e00‘ || v > ‘\u9fbf‘ {  // 该范围下的字符为非中文字符

}

}

【备注】:

这里由变量matched充当了标志,当出现非中文字符时,matched被赋为false,并使用break中止第二层循环,然后判断该name是否仅为中文,若是则添加进入targetCountw集合中。

运行结果为:map[春风:5]

§问题2. 从这个集合中找出只包含中文的用户昵称的计数信息,但发现第一个非全中文的用户昵称的时候就停止查找。

这里涉及到map类型的特性问题,当使用for进行遍历users集合时,顺序会出现不确定性,即第一个昵称有可能是“chen”,也有可能是“春风”,也有可能是“龙ge”,总之是不确定的。

假设第一个昵称是“chen长”时,程序发现它是非全中文的昵称,这时整个程序需要结束。

for name, count := range users {
    matched := true
    for _, v := range name {
        if v < ‘\u4e00‘ || v > ‘\u9fbf‘ {
            matched = false
            break
        }
    }
    if !matched {
        break
    } else {
        targetCount[name] = count
    }
}
fmt.Println(targetCount)

这个程序实现与上面基本是大同小异,不同之处在于第一层循环中的matched判断。

假设在第一层for循环中,第一次取到的昵称name为“龙ge”,此时第二层for循环中会逐个字符地查看“龙ge”这个字符串,当程序发现“龙ge”是非全中文时,matched标志被置为false,同时跑出第二层循环,然后在第一层循环中问:“取到的昵称‘龙ge’是否为全中文?”,结果不是,于是按照题目要求就break掉程序并时跳出第一层循环。

这里如果您多运行一下程序,就会发现结果有可能是不同的,有时为map[],有时为map[春风:5]

§问题3. 不使用辅助标识(如上例中的matched)解决§问题2.

作者这里是让您学习break Label这个东东,所以这里直接引用作者原话了:“我们之前说过,break语句可以与标记(Label)语句一起配合使用。”

L:
    for name, count := range users {
        for _, v := range name {
            if v < ‘\u4e00‘ || v > ‘\u9fbf‘ {
                 break L
            }
        }
        targetCount[name] = count
    }

该程序与§问题2程序功能是相同的,只是把标志matched移除掉了。

一条标记语句可以成为goto语句、break语句和contiune语句的目标。标记语句中的标记只是一个标识符,它可以被置在任何语句的左边以作为这个语句的标签。标记和被标记的语句之间需要用冒号“:”来分隔,即如下所示:

L:

for name, count := range users{

// .....

}

需要注意的是,标记L也是一个标识符,那么当它在未被使用的时候同样也会报告编译错误。那么怎么使用呢?一种方法就是让它成为break的目标,即上面示例中的break L。

标签L代码块包含了第一层for循环,第一层for循环包含了第二层for循环,所以当break L时,它是中止整个标签L代码块,所以这里会中止两层for循环。

对比“§问题2”和“§问题3”的两个代码实现,“§问题3”的代码更简洁一些。

§问题4.  还是解决问题1的事情,只不过要求使用continue语句,即从这个集合中找出只包含中文的用户昵称的计数信息

由于这里没有更多的知识点,所以引用原文:

实际上,Go语言中的continue语句只能在for语句中使用。continue语句会使直接包含它的那个for循环直接进入下一次迭代,换言之,本次迭代不会执行该continue语句后面那些语句(它们被跳过了)。

for name, count := range users {
    matched := true
    for _, v := range name {
        if v < ‘\u4e00‘ || v > ‘\u9fbf‘ {
            matched = false
            break
        }
    }

    if !matched {
       continue
    }
    targetCount[name] = count
}

或者

L:
    for name,count := range users{
        for _, v := range name{
            if r < ‘\u4e00‘ || r > ‘\u9fbf‘{
                continue L
            }
        }
        targetCount[name] = count
    }

OK,在读这个章节时,只是感觉这个例子有点意思,所以分享给大家 :)

时间: 2024-10-13 23:17:34

读《Go并发编程实战》第4章 流程控制方式的相关文章

《Java并发编程实战》第二章 线程安全性 读书笔记

一.什么是线程安全性 编写线程安全的代码 核心在于要对状态访问操作进行管理. 共享,可变的状态的访问 - 前者表示多个线程访问, 后者声明周期内发生改变. 线程安全性 核心概念是正确性.某个类的行为与其规范完全一致. 多个线程同时操作共享的变量,造成线程安全性问题. * 编写线程安全性代码的三种方法: 不在线程之间共享该状态变量 将状态变量修改为不可变的变量 在访问状态变量时使用同步 Java同步机制工具: synchronized volatile类型变量 显示锁(Explicit Lock

Java并发编程实战 第16章 Java内存模型

什么是内存模型 JMM(Java内存模型)规定了JVM必须遵循一组最小保证,这组保证规定了对变量的写入操作在何时将对其他线程可见. JMM为程序中所有的操作定义了一个偏序关系,称为Happens-Before.两个操作缺乏Happens-Before关系,则Jvm会对它们进行任意的重排序. Happends-Before的规则包括: 1. 程序顺序规则.若程序中操作A在操作B之前,则线程中操作A在操作B之前执行. 2. 监视器锁规则.在同一监视器锁上的解锁操作必须在加锁操作之前执行.如图所示,

JAVA并发编程实战---第三章:对象的共享(2)

线程封闭 如果仅仅在单线程内访问数据,就不需要同步,这种技术被称为线程封闭,它是实现线程安全性的最简单的方式之一.当某个对象封闭在一个线程中时,这种方法将自动实现线程安全性,即使被封闭的对象本生不是线程安全的. 实现好的并发是一件困难的事情,所以很多时候我们都想躲避并发.避免并发最简单的方法就是线程封闭.什么是线程封闭呢? 就是把对象封装到一个线程里,只有这一个线程能看到此对象.那么这个对象就算不是线程安全的也不会出现任何安全问题.实现线程封闭有哪些方法呢? 1:ad-hoc线程封闭 这是完全靠

JAVA并发编程实战---第三章:对象的共享

在没有同步的情况下,编译器.处理器以及运行时等都可能对操作的执行顺序进行一些意想不到的调整.在缺乏足够同步的多线程程序中,要对内存操作的执行顺序进行判断几乎无法得到正确的结果. 非原子的64位操作 当线程在没有同步的情况下读取变量时,可能会读到一个失效值,但至少这个值是由之前的某个线程设置,而不是一个随机值.这种安全性保证也被称为最低安全性. Java内存模型要求:变量的读取操作和写入操作都必须是原子操作,但对于非Volatile类型的long和Double变量,JVM允许将64的读操作或写操作

Java并发编程实战 第15章 原子变量和非阻塞同步机制

非阻塞的同步机制 简单的说,那就是又要实现同步,又不使用锁. 与基于锁的方案相比,非阻塞算法的实现要麻烦的多,但是它的可伸缩性和活跃性上拥有巨大的优势. 实现非阻塞算法的常见方法就是使用volatile语义和原子变量. 硬件对并发的支持 原子变量的产生主要是处理器的支持,最重要的是大多数处理器架构都支持的CAS(比较并交换)指令. 模拟实现AtomicInteger的++操作 首先我们模拟处理器的CAS语法,之所以说模拟,是因为CAS在处理器中是原子操作直接支持的.不需要加锁. public s

《Java并发编程实战》第九章 图形用户界面应用程序界面 读书笔记

一.为什么GUI是单线程化 传统的GUI应用程序通常都是单线程的. 1. 在代码的各个位置都须要调用poll方法来获得输入事件(这样的方式将给代码带来极大的混乱) 2. 通过一个"主事件循环(Main Event Loop)"来间接地运行应用程序的全部代码. 假设在主事件循环中调用的代码须要非常长时间才干运行完毕,那么用户界面就会"冻结",直到代码运行完毕.这是由于仅仅有当运行控制权返回到主事件循环后,才干处理兴许的用户界面事件. 非常多尝试多线程的GUI框架的努力

java并发编程实战-第2章-线程安全性

2. 线程安全性 2.1 什么是线程安全性 线程安全类:当一个类被多个线程访问时,不管运行环境中如何调度,这些线程如何交替执行,并且在调用的代码部分不需要额为的同步或者协同.这个类为线程安全类 Thread-safe classes encapsulate any needed synchronization so that clients need not provide their own. 2.1.1. Example: A Stateless Servlet Stateless obje

[笔记][Java7并发编程实战手册]第三章-线程同步辅助类-概要

[笔记][Java7并发编程实战手册]系列目录 有点着急了,没有太注重质量,自己也没有理解透,从本章起,读书和随笔笔记的质量会更好. 第三章 在本章中,我们将学习: 资源的并发访问控制 资源的多副本的并发访问控制 等待多个并发事件的完成 在集合点的同步 并发阶段任务的运行 并发阶段任务中的阶段交换 并发任务间的数据交换 回顾 在第二章中主要学习了以下接口 synchronized关键字 Lock接口以及实现类,如ReentrantLock.ReentrantReadWriteLock中的Read

《Java并发编程实战》第十六章 Java内存模型 读书笔记

Java内存模型是保障多线程安全的根基,这里仅仅是认识型的理解总结并未深入研究. 一.什么是内存模型,为什么需要它 Java内存模型(Java Memory Model)并发相关的安全发布,同步策略的规范.一致性等都来自于JMM. 1 平台的内存模型 在架构定义的内存模型中将告诉应用程序可以从内存系统中获得怎样的保证,此外还定义了一些特殊的指令(称为内存栅栏或栅栏),当需要共享数据时,这些指令就能实现额外的存储协调保证. JVM通过在适当的位置上插入内存栅栏来屏蔽在JVM与底层平台内存模型之间的