【技术思路】极客时间-左耳听风-开篇词2

11 | 程序中的错误处理:错误返回码和异常捕捉

传统的错误检查

  • 传统的方式:处理错误最直接的方式是经过错误码。

多返回值

因而,有一些语言经过多返回值来解决这个问题,好比 Go 语言。Go 语言的不少函数都会返回result, err 两个值。javascript

  • 参数上基本上就是入参,而返回接口把结果和错误分离,这样使得函数的接口语义清晰;
  • Go 语言中的错误参数若是要忽略,须要显式地忽略,用_ 这样的变量来忽略;html

  • 由于返回的 error 是个接口(其中只有一个方法Error() ,返回一个 string ),因此能够扩展自定义的错误处理。java

资源清理

  • C语言方式:

这样的处理方式虽然能够,可是会有潜在的问题。最主要的一个问题就是你不能在中间的代码中有 return 语句,由于你须要清理资源。在维护这样的代码时须要格外当心,由于一不注意就会致使代码有资源泄漏的问题。python

#define FREE(p) if(p) { \
                    free(p); \
                    p = NULL; \
                 }
main()
{
    char *fname=NULL, *lname=NULL, *mname=NULL;
    fname = ( char* ) calloc ( 20, sizeof(char) );
    if ( fname == NULL ){
        goto fail;
    }
    lname = ( char* ) calloc ( 20, sizeof(char) );
    if ( lname == NULL ){
        goto fail;
    }
    mname = ( char* ) calloc ( 20, sizeof(char) );
    if ( mname == NULL ){
        goto fail;
    }
    
    ......
 
    
fail:
    FREE(fname);
    FREE(lname);
    FREE(mname);
    ReportError(ERR_NO_MEMORY);
}
  • C++方式

C++ 的 RAII(Resource Acquisition Is Initialization)机制使用面向对象的特性能够容易地处理这个事情。RAII 使用 C++ 类的机制,在构造函数中分配资源,在析构函数中释放资源。git

// 首先,先声明一个 RAII 类,注意其中的构造函数和析构函数
class LockGuard {
public:
    LockGuard(std::mutex &m):_m(m) { m.lock(); }
    ~LockGuard() { m. unlock(); }
private:
    std::mutex& _m;
}

// 而后,咱们来看一下,怎样使用的
void good()
{
    LockGuard lg(m);                // RAII 类:构造时,互斥量请求加锁
    f();                             // 若 f() 抛异常,则释放互斥
    if(!everything_ok()) return;     // 提前返回,LockGuard 析构时,互斥量被释放
}                                    // 若 good() 正常返回,则释放互斥
  • Go语言

在 Go 语言中,使用defer关键字也能够作到这样的效果:github

func Close(c io.Closer) {
    err := c.Close()
    if err != nil {
        log.Fatal(err)
    }
}

func main() {
    r, err := Open("a")
    if err != nil {
        log.Fatalf("error opening 'a'\n")
    }
    defer Close(r) // 使用 defer 关键字在函数退出时关闭文件。

    r, err = Open("b")
    if err != nil {
        log.Fatalf("error opening 'b'\n")
    }
    defer Close(r) // 使用 defer 关键字在函数退出时关闭文件。
}

异常捕捉处理

try - catch - finally编程模式。算法

try {
    ... // 正常的业务代码
} catch (Exception1 e) {
    ... // 处理异常 Exception1 的代码
} catch (Exception2 e) {
    ... // 处理异常 Exception2 的代码
} finally {
    ... // 资源清理的代码
}

这样的异常处理方式有以下一些好处。编程

  • 函数接口在 input(参数)和 output(返回值)以及错误处理的语义是比较清楚的。
  • 正常逻辑的代码能够与错误处理和资源清理的代码分开,提升了代码的可读性。
  • 异常不能被忽略(若是要忽略也须要 catch 住,这是显式忽略)。
  • 在面向对象的语言中(如 Java),异常是个对象,因此,能够实现多态式的 catch。
  • 与状态返回码相比,异常捕捉有一个显著的好处是,函数能够嵌套调用,或是链式调用。好比:int x = add(a, div(b,c));Pizza p = PizzaBuilder().SetSize(sz) .SetPrice(p)...;

但有个致命的问题,那就是在异步运行的世界里的问题。try 语句块里的函数运行在另一个线程中,其中抛出的异常没法在调用者的这个线程中被捕捉。promise

错误返回码 vs 异常捕捉

前面也比较过二者的优缺点,整体而言,彷佛异常捕捉的优点更多一些。应该从场景上来讨论这个事才是正确的姿式。错误可分为三个大类:网络

  • 资源的错误:程序运行环境的问题,代码去请求一些资源时致使的错误,好比打开一个没有权限的文件,写文件时出现的写错误,发送文件到网络端发现网络故障等错误。关键性资源不能知足的状况,内存耗尽、栈溢出、一些程序运行时关键性资源不能知足等。

  • 程序的错误:好比:空指针、非法参数等。这类是本身程序的错误,要记录下来,写入日志,最好触发监控系统报警
  • 用户的错误:在用户的API层上出现的问题,好比解析一个XML和JSON文件,或是用户输入的字段不合法类的错误。

能够这样来在逻辑上分类:

  • 对于咱们并不指望会发生的事,咱们可使用异常捕捉;
  • 对于咱们以为可能会发生的事,使用返回码。

12 | 程序中的错误处理:异步编程以及个人最佳实践

异步编程世界里的错误处理

在异步编程的世界里,由于被调用的函数是被放到了另一个线程里运行,这将致使:

  • 没法使用返回码:由于函数在异步状态中,只是把返回的处理权交给下一条指令,而不是把函数运行完的结果返回。
  • 没法使用抛异常的方式:由于除了上述的函数立马返回的缘由以外,抛出的异常也在另一个线程中,不一样线程中的栈是彻底不同的,因此主线程的 catch 彻底看不到另一个线程中的异常。

几种处理错误的方法:

对此,在异步编程的世界里,咱们也会有好几种处理错误的方法,最经常使用的就是callback 方式。在作异步请求的时候,注册几个 OnSuccess()OnFailure()的函数,让在另外一个线程中运行的异步代码来回调回来。

JavaScript 异步编程的错误处理

function successCallback(result) {
  console.log("It succeeded with " + result);
}

function failureCallback(error) {
  console.log("It failed with " + error);
}

doSomething(successCallback, failureCallback);

经过注册错误处理的回调函数,让异步执行的函数在出错的时候,调用被注册进来的错误处理函数,这样的方式比较好地解决了程序的错误处理。而出错的语义从返回码、异常捕捉到了直接耦合错误出处函数的样子。

Java 异步编程的 Promise 模式

在 Java 中,在 JDK 1.8 里也引入了相似 JavaScript 的玩法 —— CompletableFuture

链式处理:

CompletableFuture.supplyAsync(this::findReceiver)
                 .thenApply(this::sendMsg)
                 .thenAccept(this::notify);

Go 语言的 Promise

首先,先声明一个结构体。其中有三个成员:第一个 wg 用于多线程同步;第二个 res 用于存放执行结果;第三个 err 用于存放相关的错误。

type Promise struct {
    wg  sync.WaitGroup
    res string
    err error
}

而后,定义一个初始函数,来初始化 Promise 对象。其中能够看到,须要把一个函数 f 传进来,而后调用 wg.Add(1) 对 waitGroup 作加一操做,新开一个 Goroutine 经过异步去执行用户传入的函数 f() ,而后记录这个函数的成功或错误,并把 waitGroup 作减一操做。

func NewPromise(f func() (string, error)) *Promise {
    p := &Promise{}
    p.wg.Add(1)
    go func() {
        p.res, p.err = f()
        p.wg.Done()
    }()
    return p
}

而后须要定义 Promise 的 Then 方法。其中须要传入一个函数,以及一个错误处理的函数。而且调用 wg.Wait() 方法来阻塞(由于以前被wg.Add(1)),一旦上一个方法被调用了 wg.Done(),这个 Then 方法就会被唤醒。

唤醒的第一件事是,检查一下以前的方法有没有错误。若是有,那么就调用错误处理函数。若是以前成功了,就把以前的结果以参数的方式传入到下一个函数中。

func (p *Promise) Then(r func(string), e func(error)) (*Promise){
    go func() {
        p.wg.Wait()
        if p.err != nil {
            e(p.err)
            return 
        }
        r(p.res)
    }()
    return p
}

下面,定义一个用于测试的异步方法。这个方面很简单,就是在数数,而后,有一半的机率会出错。

func exampleTicker() (string, error) {
    for i := 0; i < 3; i++ {
        fmt.Println(i)
        <-time.Tick(time.Second * 1)
    }
    
    rand.Seed(time.Now().UTC().UnixNano())
    r:=rand.Intn(100)%2
    fmt.Println(r)
    if  r != 0 {
        return "hello, world", nil
    } else {
        return "", fmt.Errorf("error")
    }
}

下面,来看看实现的 Go 语言 Promise 是怎么使用的。

func main() {
    doneChan := make(chan int)
    
    var p = NewPromise(exampleTicker)
    p.Then(func(result string) { fmt.Println(result); doneChan <- 1 }, 
            func(err error) { fmt.Println(err); doneChan <-1 })
            
    <-doneChan
}

错误处理的最佳实践

  • 统一分类的错误字典:创建一个错误字典,不管是使用错误码仍是异常捕捉,都须要认真并统一地作好错误的分类。最好是在一个地方定义相关的错误。好比,HTTP 的 4XX 表示客户端有问题,5XX 则表示服务端有问题。
  • 同类错误的定义最好是能够扩展的:经过面向对象的继承或是像 Go 语言那样的接口多态能够很好重用已有的代码。
  • 定义错误的严重程度:好比Fatal 表示重大错误,Error 表示资源或需求得不到知足,Warning 表示并不必定是个错误但仍是须要引发注意,Info 表示不是错误只是一个信息,Debug 表示这是给内部开发人员用于调试程序的。
  • 错误日志的输出最好使用错误码,而不是错误信息:打印错误日志的时候,应该使用统一的格式。但最好不要用错误信息,而应使用相应的错误码,错误码不必定是数字,也能够是一个能从错误字典里找到的一个惟一的可让人读懂的关键字。这样会很是有利于日志分析软件进行自动化监控,而不是要从错误信息中作语义分析。好比:HTTP 的日志中就会有 HTTP 的返回码,如:404。使用PageNotFound这样的标识,这样人和机器都很容易处理。
  • 忽略错误最好有日志:否则会给维护带来很大的麻烦。
  • 对于同一个地方不停的报错,最好不要都打到日志里:否则这样会致使其它日志被淹没了,也会致使日志文件太大。最好的实践是,打出一个错误以及出现的次数。

  • 不要用错误处理逻辑来处理业务逻辑:不要使用异常捕捉这样的方式来处理业务逻辑,而是应该用条件判断。若是一个逻辑控制能够用 if - else 清楚地表达,那就不建议使用异常方式处理。异常捕捉是用来处理不指望发生的事情,而错误码则用来处理可能会发生的事。

  • 对于同类的错误处理,用同样的模式:好比,对于null对象的错误,要么都用返回 null,加上条件检查的模式,要么都用抛 NullPointerException 的方式处理。不要混用,这样有助于代码规范。
  • 尽量在错误发生的地方处理错误:由于这样会让调用者变得更简单。

  • 向上尽量地返回原始的错误:若是必定要把错误返回到更高层去处理,那么应该返回原始的错误,而不是从新发明一个错误。
  • 处理错误时,老是要清理已分配的资源:使用 RAII 技术,或是 try-catch-finally,或是 Go 的 defer 均可以容易地作到。
  • 不推荐在循环体里处理错误:这里说的是 try-catch,绝大多数的状况你不须要这样作。最好把整个循环体外放在 try 语句块内,而在外面作 catch。
  • 不要把大量的代码都放在一个 try 语句块内:一个 try 语句块内的语句应该是完成一个简单单一的事情。
  • 为你的错误定义提供清楚的文档以及每种错误的代码示例:若是你是作 RESTful API 方面的,使用 Swagger 会帮你很容易搞定这个事。
  • 对于异步的方式,推荐使用 Promise 模式处理错误:对于这一点,JavaScript 中有很好的实践。
  • 对于分布式的系统,推荐使用 APM 相关的软件:尤为是使用 Zipkin 这样的分布式跟踪系统来帮助收集时间数据关联错误。

13 | 魔数 0x5f3759df

数学的基础知识。数学真是须要努力学习好的一门功课,尤为在人工智能火热的今天。

可是对于系统化学习追求者来讲,这篇文章并不具有必定的视野价值。

14 | 推荐阅读:机器学习101

在机器学习(Machine learning)领域,主要有三类不一样的学习方法:

监督学习(Supervised learning)、

非监督学习(Unsupervised learning)、

半监督学习(Semi-supervised learning),

监督学习:经过已有的一部分输入数据与输出数据之间的对应关系,生成一个函数,将输入映射到合适的输出,例如分类。 
非监督学习:直接对输入数据集进行建模,例如聚类。 

半监督学习:综合利用有类标的数据和没有类标的数据,来生成合适的分类函数。

文章中做者描述机器学习主要来讲有两种方法,监督式学习(Supervised Learning)和非监督式学习。

监督式学习

提供一组学习样本,包括相关的特征数据以及相应的标签。程序能够经过这组样原本学习相关的规律或是模式,而后经过获得的规律或模式来判断没有被打过标签的数据是什么样的数据。

在监督式学习下,须要有样本数据或是历史数据来进行学习,这种方式会有一些问题。好比

  • 若是一个事物没有历史数据,那么就很差作了。变通的解决方式是经过一个和其相似事物的历史数据。我之前作过的需求预测,就属于这种状况。对于新上市的商品来讲,彻底没有历史数据,好比,iPhone X,那么就须要从其相似的商品上找历史数据,如 iPhone 7 或是别的智能手机。
  • 历史数据中可能会有一些是噪音数据,须要把这些噪音数据给过滤掉。通常这样的过滤方式要经过人工判断和标注。举两个例子,某名人在其微博或是演讲上推荐了一本书,因而这本书的销量就上升了。这段时间的历史数据不是规律性的,因此就不能成为样本数据,须要去掉。一样,若是某名人(如 Michael Jackson)去世致使和其有关的商品销售量很好,那么,这个事件所产生的数据则不属于噪音数据。由于每一年这个名人忌日的时候出现销量上升的可能性很是高,因此,须要标注一下,这是有规律的样本,能够放入样本进行学习。

非监督式学习

对于非监督式学习,也就是说,数据是没有被标注过的,因此相关的机器学习算法须要找到这些数据中的共性。由于大量的数据是没有被标识过的,因此这种学习方式可让大量未标识的数据可以更有价值。

好比,一个在某一年龄段区间的女生购买了某种肥皂,有可能说明这个女生在怀孕期,或是某人购买儿童用品,有可能说明这我的的关系链中有孩子,等等。因而这些信息会被用做一些所谓的精准市场营销活动,从而能够增长商品销量。

在监督式的学习的算法下,咱们能够用一组“狗”的照片来肯定某个照片中的物体是否是狗。而在非监督式的学习算法下,咱们能够经过一个照片来找到与其类似事物的照片。这两种学习方式都有各自适用的场景。

如何找到数据的规律和关联

机器学习基本就是在已知的样本数据中寻找数据的规律,在未知的数据中找数据的关系。

机器学习中的基本方法论

  • 要找到数据中的规律,你须要找到数据中的特征点。
  • 把特征点抽象成数学中的向量,也就是所谓的坐标轴。一个复杂的学习可能会有成十上百的坐标轴。
  • 抽象成数学向量后,就能够经过某种数学公式来表达这类数据(就像 y=ax+b 是直线的公式),这就是数据建模。

机器学习文章:

Machine Learning is Fun!
https://medium.com/@ageitgey/machine-learning-is-fun-80ea3ec3c471
中文翻译版
https://zhuanlan.zhihu.com/p/24339995

相关算法

对于监督式学习,有以下经典算法。

  1. 决策树(Decision Tree)。好比自动化放贷、风控。
  2. 朴素贝叶斯分类(Naive Bayesian classification)。能够用于判断垃圾邮件,对新闻的类别进行分类,好比科技、政治、运动,判断文本表达的感情是积极的仍是消极的,以及人脸识别等。
  3. 最小二乘法(Ordinary Least Squares Regression)。算是一种线性回归。
  4. 逻辑回归(Logisitic Regression)。一种强大的统计学方法,能够用一个或多个变量来表示一个二项式结果。它能够用于信用评分、计算营销活动的成功率、预测某个产品的收入等。
  5. 支持向量机(Support Vector Machine,SVM)。能够用于基于图像的性别检测,图像分类等。
  6. 集成方法(Ensemble methods)。经过构建一组分类器,而后根据它们的预测结果进行加权投票来对新的数据点进行分类。原始的集成方法是贝叶斯平均,可是最近的算法包括纠错输出编码、Bagging 和 Boosting。

对于非监督式的学习,有以下经典算法。

  1. 聚类算法(Clustering Algorithms)。聚类算法有不少,目标是给数据分类。
  2. 主成分分析(Principal Component Analysis,PCA)。PCA 的一些应用包括压缩、简化数据,便于学习和可视化等。
  3. 奇异值分解(Singular Value Decomposition,SVD)。实际上,PCA 是 SVD 的一个简单应用。在计算机视觉中,第一我的脸识别算法使用 PCA 和 SVD 来将面部表示为“特征面”的线性组合,进行降维,而后经过简单的方法将面部匹配到身份。虽然现代方法更复杂,但不少方面仍然依赖于相似的技术。
  4. 独立成分分析(Independent Component Analysis,ICA)。ICA 是一种统计技术,主要用于揭示随机变量、测量值或信号集中的隐藏因素。

上面的这些相关算法来源自博文《The 10 Algorithms Machine Learning Engineers Need to Know》。

https://www.kdnuggets.com/2016/08/10-algorithms-machine-learning-engineers.html

相关推荐

学习机器学习有几个课是必需要上的,具体以下。

除了上述的那些课程外,下面这些资源也很不错。

值得翻阅的图书。

  • 《机器学习》,南京大学周志华教授著。它是一本机器学习方面的入门级教科书,适合本科三年级以上的学生学习。
  • A Course In Machine Learning,马里兰大学哈尔·道姆(Hal Daumé III)副教授著。 这本书讲述了几种经典机器学习算法,包括决策树、感知器神经元、kNN 算法、K-means 聚类算法、各类线性模型(包括对梯度降低、支持向量机等的介绍)、几率建模、神经网络、非监督学习等不少主题,还讲了各类算法使用时的经验技巧,适合初学者学习。此外,官网还提供了免费电子版。
  • Deep Learning,麻省理工学院伊恩·古德费洛(Ian Goodfellow)、友华·本吉奥(Yoshua Benjio)和亚伦·考维尔(Aaron Courville)著。这本书是深度学习专题的经典图书。它从历史的角度,将读者带进深度学习的世界。深度学习使用多层的(深度的)神经元网络,经过梯度降低算法来实现机器学习,对于监督式和非监督式学习都有大量应用。若是读者对该领域有兴趣,能够深刻阅读本书。本书官网提供免费电子版,但不提供下载。实体书(英文原版或中文翻译版)能够在网上买到。
  • Reinforcement Learning,安德鲁·巴托(Andrew G.Barto)和理查德·萨顿(Richard S. Sutton)著。这本书是强化学习(Reinforcement Learning)方面的入门书。它覆盖了马尔可夫决策过程(MDP)、Q-Learning、Sarsa、TD-Lamda 等方面。这本书的做者是强化学习方面的创始人之一。强化学习(结合深度学习)在围棋程序 AlphaGo 和自动驾驶等方面都有着重要的应用。
  • Pattern Recognition and Machine Learning ,微软剑桥研究院克里斯托夫·比肖普(Christoph M. Bishop)著。这本书讲述了模式识别的技术,包括机器学习在模式识别中的应用。模式识别在图像识别、天然语言处理、控制论等多个领域都有应用。平常生活中扫描仪的 OCR、平板或手机的手写输入等都属于该领域的研究。

15 | 时间管理:同扭曲时间的事儿抗争

主动管理

  • 你要主动管理的不是你的时间,而是管理你的同事,管理你的信息

仿照一下之前在 Outlook 里设置工做日程的方式,把你的工做安排预先设置到一个能够共享的日历上,而后分享给你们,让你们了解你的日程。

学会说“不”

  • 给出另外一个你能够作到的方案,而不是把对方的方案直接回绝掉
  • 我不说我不能彻底知足你,但我说我能够部分知足你

我不能说不,可是我要有条件地说是。并且,我要把你给个人压力再反过来还给你,看似我给了需求方选择,实际上,我掌握了主动。

“积极主动的态度下对于不合理的事讨价还价”。只有学会了说“不”,你才可以控制好你的时间。

加班和开会

开会,不是讨论问题,而是讨论方案,开会不是要有议题,而是要有议案。

16 | 时间管理:如何利用好本身的时间?

投资本身的时间

  • 花时间学习基础知识,花时间读文档:系统地学习一门技术是很是关键的,因此这个时间是值得投资的。
  • 花时间在解放本身生产力的事上:花时间在解放本身的事上是最有意义的了
  • 花时间在让本身成长的事上
  • 花时间在创建高效的环境上

规划本身的时间

  • 定义好优先级:有 to-do list 并非什么高深的事。更重要的是,你要知道什么事是重要的,什么事是紧急的,什么事重要但不紧急,什么事又重要又紧急。
  • 最短做业优先:先把能够快速作完的事作完,看到 to-do list 上划掉一个任务,看到任何的数据在减小,对于本身也好,对于老板也好。老板能够看到你的工做进度飞快,一方面有利于为后面复杂的工做争取更多的时间。
  • 想清楚再作:对于一些没想清楚的事,或是本身不太有信心的事,仍是先看看有没有已有的成熟解决方案或是找更牛的人来给你把把关,帮你出出主意,看看有没有更好、更简单的方式。
  • 关注长期利益规划:要多关注长远能够节省多少时间,而不是当前会花费多少时间。长期成本会比短时间成本大得多。因此宁肯在短时间延期,也不要透支将来。学会规划本身的行动计划,不是短时间的,而是一个中长期的。按季度来规划,这个季度作什么,达到什么目标,一年往前走四步,而不是只考虑眼下。

用好本身的时间

  • 将军赶路不追小兔:学会过滤掉与本身目标无关的事,不要让那些无关的事控制本身。知道哪些是更有效的路径,是花时间改变别人,仍是花时间去寻找志同道合的人。不与不如本身的人争论,也不要尝试花时间去叫醒那些装睡的人,这些都是很是浪费时间的事。多花时间在有产出的事上,少花时间在说服别人的事上。
  • 造成习惯:将文章中提到的方法和几个要点,写在某本书或者笔记本的扉页上,方便查看,时刻提醒本身。
  • 造成正反馈:要让本身有正反馈,那就须要把时间花在有价值的地方,好比,解决本身和他人的痛点,这样你会收获别人的赞赏和鼓励。
  • 反思和触类旁通:能够尝试每周末花上点时间思考一下,本周作了哪些事儿?时间安排是否合理?还有哪些能够优化提升的地方?有点儿相似于咱们常说的“复盘”。而后思考一下,下周的主要任务是什么?并根据优先级规划一下完成这些任务的顺序,也就是作一些下周的工做规划。

17 | 故障处理最佳实践:应对故障

目前没有遇到实际场景,略过。

18 | 故障处理最佳实践:故障改进

目前没有遇到实际场景,略过。

19 | 答疑解惑:咱们应该可以识别的表象和本质

本质上来讲,并非只有找到了相应的工做咱们才能够学好一项技术,而是,咱们在经过解决实际问题,在和他人讨论,得到高手帮助的环境中,才能更快更有效率地学习和成长。

关于技术和价值

一项有价值的技术,并不在于这项技术是否有技术含量,而是在于:

  • 可否低成本高效率地解决实际问题;

软件天生就是用来完成重复劳动的,天生就是用来作自动化的。

  • 是否是众多产品的基础技术;

基础技术老是枯燥和有价值的。数学、算法、网络、存储等基础技术吃得越透,就越容易服务上层的各类衍生技术或产品。

  • 是否是能够支持规模化的技术。

能规模化低成本高效率解决实际问题的技术及其基础技术,就算是很 low,也是颇有价值的。

### 关于趋势和将来

这个世界的技术趋势和将来实际上是被人控制的,因此对于咱们这些在这个世界里排不上号的人来讲,只能默默地跟随着这些大公司所引领的趋势和将来。对一些缺钱缺人的创业公司,惟一可以作的,也许只是两条路,一是用更为低的成原本提供和大公司相应的技术,另外一条路是在细分垂直市场上作得比大公司更专更精。等着本身有一天长大后,也能加入第一梯队从而“引领”将来。

20 | Git协同工做流,你该怎么选?

Git一直在使用,略过。

相关文章
相关标签/搜索