ARTS #004

ARTS #004

Posted by Dan on August 24, 2018

ARTS是由左耳朵耗子--陈皓发起的一个活动: 每周至少做一个leetcode的算法题、阅读并点评至少一篇英文技术文章、学习至少一个技术技巧、分享一篇有观点和思考的文章。(也就是Algorithm、Review、Tip、Share简称ARTS),至少坚持一年。

ARTS 004

这是第4篇,写的比较水,希望以后越来越好。

Algorihm 算法题

leetcode算法第321题 Create Maximum Number(拼接最大数) 难道:困难

Given two arrays of length m and n with digits 0-9 representing two numbers. Create the maximum number of length k <= m + n from digits of the two. The relative order of the digits from the same array must be preserved. Return an array of the k digits.

Note: You should try to optimize your time and space complexity.

Example 1:

Input:
nums1 = [3, 4, 6, 5]
nums2 = [9, 1, 2, 5, 8, 3]
k = 5
Output:
[9, 8, 6, 5, 3]
Example 2:

Input:
nums1 = [6, 7]
nums2 = [6, 0, 4]
k = 5
Output:
[6, 7, 6, 0, 4]
Example 3:

Input:
nums1 = [3, 9]
nums2 = [8, 9]
k = 3
Output:
[9, 8, 9]


给定长度分别为 m 和 n 的两个数组,其元素由 0-9 构成,表示两个自然数各位上的数字。现在从这两个数组中选出 k (k <= m + n) 个数字拼接成一个新的数,要求从同一个数组中取出的数字保持其在原数组中的相对顺序。

求满足该条件的最大数。结果返回一个表示该最大数的长度为 k 的数组。

说明: 请尽可能地优化你算法的时间和空间复杂度。

示例 1:

输入:
nums1 = [3, 4, 6, 5]
nums2 = [9, 1, 2, 5, 8, 3]
k = 5
输出:
[9, 8, 6, 5, 3]
示例 2:

输入:
nums1 = [6, 7]
nums2 = [6, 0, 4]
k = 5
输出:
[6, 7, 6, 0, 4]
示例 3:

输入:
nums1 = [3, 9]
nums2 = [8, 9]
k = 3
输出:
[9, 8, 9]

解题思路:

  • 1 确定两个数组各取几个数,比如k=5,可以拆成1,4;2,3;3,2;4,1;而且要小于nums1或nums2的个数。这个一步比较简单。
  • 2 把符合条件的子数组合并为一个 这一步就比较复杂了。 比如nums1 = [6, 7, 0];nums2 = [6, 0, 4],k=5; k这个就可以拆成2,3;3,2; 现在考虑2,3的情况,nums1_1 = [6, 7];nums2_1 = [6, 0, 4]; 那么怎么把这两个子数组合成一个最大数呢;咋一看还挺简单,把两个数组各取一个,哪个大取哪一个不就可以了吗,但是这里有一个坑就是大小一样怎么办?比如nums1_1和nums2_1的第一个数都是6,取哪一个呢?我们看看有什么不一样。 如果第一次取nums2_1的6,取完之后nums1_1 = [6, 7];nums2_1 = [0, 4]; 第二次nums1_1取6,nums2_1只能取0,6比0大,所以取6,取完之后nums1_1 = [7];nums2_1 = [0, 4];,最终的结果就是[6,6,7,0,4]

    如果第一次取nums1_1的6,取完之后nums1_1 = [7];nums2_1 = [6,0, 4]; 第二次nums1_1取7,nums2_1取6,7比6大,所以取7,取完之后nums1_1 = [];nums2_1 = [6,0, 4];,最终的结果就是[6,7,6,0,4]

    结果不一样,显然第二个是对的。 上面我们用的合并方法是不是和归并排序中用到的算法很像,但是归并排序中合并数组的时候,两个数组是有序的,我们这里合并的两个数组是无序的,所以就会遇到这个问题,我的实习代码如下:

  /**
 * Return an array of size *returnSize.
 * Note: The returned array must be malloced, assume caller calls free().
 */
int maxInexd(int* nums,int max,int min){
    int maxindex = min;
    int count = nums[maxindex];
    for (int i = min; i<=max; i++) {
        if (nums[i] > count) {
            count = nums[i];
            maxindex = i;
        }
    }
    return maxindex;
}

int* maxNumber1(int* nums, int numsSize, int numcount){
    int *a = (int *)malloc(numcount*sizeof(int));
    int index = -1;
    for (int i = 0; i < numcount; i++) {
        index = maxInexd(nums, numsSize - numcount +i, index+1);
        a[i] = nums[index];
    }
    return a;
}

int* merge(int* num11, int nums1count, int* num22, int nums2count, int k){
    //把两个数组合并为一个
    int* tempReturnSize = (int *)malloc(k*sizeof(int));
    int mm = 0;
    int nn = 0;
    for (int i = 0; i < k; i++) {
        if (mm == nums1count) {
            int a = num22[nn];
            tempReturnSize[i] = a;
            nn++;
            continue;
        }
        
        if (nn == nums2count) {
            int b = num11[mm];
            tempReturnSize[i] = b;
            mm++;
            continue;
        }
        
        int aa = num11[mm];
        int bb = num22[nn];
        
        if (aa >bb) {
            tempReturnSize[i] = aa;
            mm++;
        }
        else if (aa == bb){
            int aa1 = aa;
            int bb1 = bb;
            int  ii = 0;
            while (mm + ii +1 != nums1count && nn + ii+1 != nums2count && aa1 ==bb1  )  {
                ii++;
                aa1 = num11[mm + ii];
                bb1 = num22[nn + ii];
            }
            
            if (aa1 > bb1) {
                tempReturnSize[i] = aa;
                mm++;
                continue;
            }
            else if(aa1 < bb1){
                tempReturnSize[i] = bb;
                nn++;
                continue;
            }
            
            if (mm + ii +1 == nums1count) {//说明没有下一个
                tempReturnSize[i] = bb;
                nn++;
                continue;
            }
            
            if (nn + ii +1 == nums2count) {//说明没有下一个
                tempReturnSize[i] = bb;
                mm++;
                continue;
            }
        }
        else{
            tempReturnSize[i] = bb;
            nn++;
        }
    }
    
    return tempReturnSize;
}

int* maxNumber(int* nums1, int nums1Size, int* nums2, int nums2Size, int k, int* returnSize) {
   *returnSize = k; //在leetcode提交的时候,不知道为什么要加上这个
    if (nums1Size ==0) {
        return nums2;
    }
    if (nums2Size ==0) {
        return nums1;
    }
    
    int* relsut = (int*)malloc(sizeof(int) * k);
    memset(relsut, 0, sizeof(int) * k);
    int nums1count, nums2count;
    for (nums1count = 1; nums1count <= nums1Size; nums1count++) {//nums1count 表示从nums1数组取的数的个数 最少是1  最大是nums1Size
        nums2count = k - nums1count;
        if (nums2count > nums2Size) {
            continue;
        }
        if(nums2count == 0){//不能不从nums2取值
            break;
        }
        
        //从nums1中按序取出nums1count个数,且这个几个数按序组装成整数之后是最大的
        int *num11 = maxNumber1(nums1,  nums1Size,nums1count);

        //从nums2中按序取出nums2count个数,且这个几个数按序组装成整数之后是最大的
        int *num22 = maxNumber1(nums2,  nums2Size,nums2count);

        //把两个数组合并为一个
        int* tempReturnSize =merge( num11,  nums1count, num22,  nums2count, k);
        
        //比较最大的数值
        for (int i = 0; i < k; i++) {
            int aa = tempReturnSize[i];
            int bb = relsut[i];
            if (aa > bb) {
                for (int j = 0; j < k; j++){
                    relsut[j] = tempReturnSize[j];
                }
                break;
            }
            else if (bb >aa){
                break;
            }
        }
        free(tempReturnSize);
        free(num11);
        free(num22);
    }
    return relsut;
}  

在LeetCode的运行时间是20ms,下面的代码是在LeetCode提交的运行时间是8ms的代码,感觉和我的实现也差不多,不知道快在哪里:

void maxNum(int*, int, int, int*);
void merge(int*, int, int*, int, int*);
void max(int*, int*, int);

int* maxNumber(int* nums1, int nums1Size, int* nums2, int nums2Size, int k, int* returnSize)
{
    *returnSize = k;
    int* result = (int*)malloc(sizeof(int) * k);
    memset(result, 0, sizeof(int) * k);
    int* merged = (int*)malloc(sizeof(int) * k);
    int *max1 = NULL, *max2 = NULL;
    for (int i = 0; i <= k; i++) {
        int n1 = i, n2 = k - i;
        if (n1 > nums1Size || n2 > nums2Size)
            continue;
        max1 = realloc(max1, sizeof(int) * n1);
        maxNum(nums1, nums1Size, n1, max1);

        max2 = realloc(max2, sizeof(int) * n2);
        maxNum(nums2, nums2Size, n2, max2);

        merge(max1, n1, max2, n2, merged);

        max(result, merged, k);
    }
    free(max1), free(max2), free(merged);
    return result;
}

void maxNum(int* nums, int n, int k, int* r)
{
    for (int i = 0, j = 0; i < n; i++) {
        // drop last one
        while (j > 0 && k - j < n - i && nums[i] > r[j - 1])
            j--;
        if (j < k)
            r[j++] = nums[i];
    }
}
void merge(int* nums1, int n1, int* nums2, int n2, int* r)
{
    for (int i = 0, j = 0, k = 0; k < n1 + n2; k++) {
        int val1 = i < n1 ? nums1[i] : -1;
        int val2 = j < n2 ? nums2[j] : -1;
        int t = 1;
        while (val1 == val2 && val1 != -1) {
            val1 = i + t < n1 ? nums1[i + t] : -1;
            val2 = j + t < n2 ? nums2[j + t] : -1;
            t++;
        }
        if (val1 > val2)
            r[k] = nums1[i++];
        else if (val1 < val2)
            r[k] = nums2[j++];
        else
            // val1 == val2 ==  -1
            r[k] = nums1[i++];
    }
}
void max(int* nums1, int* nums2, int n)
{
    int i;
    for (i = 0; i < n; i++) {
        if (nums1[i] < nums2[i])
            break;
        if (nums1[i] > nums2[i])
            return;
    }
    if (i != n)
        // set the new max
        for (i = 0; i < n; i++)
            nums1[i] = nums2[i];
}


Review

这篇文章来自: https://medium.com/exploring-code/why-should-you-learn-go-f607681fad65

Why should you learn Go?(你为什么应该学习Go)

1_vHUiXvBE0p0fLRwFHZuAY

Image from: http://kirael-art.deviantart.com/art/Go-lang-Mascot-458285682

“Go will be the server language of the future.” — Tobias Lütke, Shopify

In past couple of years, there is a rise of new programming language: Go or GoLang. Nothing makes a developer crazy than a new programming language, right? So, I started learning Go before 4 to 5 months and here I am going to tell you about why you should also learn this new language.

在过去几年中,有一种新的编程语言崛起:Go或GoLang。没有什么能让开发人员疯狂除了新的编程语言,不是吗? 所以,我在4到5个月之前开始学习Go,在这里我将告诉你为什么你还应该学习这门新语言。

I am not going to teach you, how you can write “Hello World!!” in this article. There are lots of other articles online for that. I am going the explain current stage of computer hardware-software and why we need new language like Go? Because if there isn’t any problem, then we don’t need solution, right? 在这篇文章中,我不打算教你怎么写“Hello World !!”。 网上已经有很多文章类似的文章了。 “我现在要解释在计算机硬件、软件的当前阶段,为什么我们需要像Go这样的新语言?”,因为如果没有任何问题,那么我们不需要解决方案,对吧?

#####Hardware limitations(硬件限制): Moore’s law is failing. First Pentium 4 processor with 3.0GHz clock speed was introduced back in 2004 by Intel. Today, my Mackbook Pro 2016 has clock speed of 2.9GHz. So, nearly in one decade, there is not too much gain in the raw processing power. You can see the comparison of increasing the processing power with the time in below chart.

摩尔定律失效了 英特尔于2004年推出了第一款具有3.0GHz时钟速度的奔腾4处理器。 今天,我的Mackbook Pro 2016的时钟速度为2.9GHz。 因此,差不多十年,原始处理能力没有太大的提升。 您可以在下图中看到处理能力的增加与时间的比较。

From the above chart you can see that the single-thread performance and the frequency of the processor remained steady for almost a decade. If you are thinking that adding more transistor is the solution, then you are wrong. This is because at smaller scale some quantum properties starts to emerge (like tunneling) and because it actually costs more to put more transistors (why?) and the number of transistors you can add per dollar starts to fall.

从上图中可以看出,单线程性能和处理器频率近十年一直保持稳定。 如果您认为添加更多晶体管是解决方案,那么您错了。 这是因为在较小规模上,一些量子特性开始出现(如隧道效应),并且因为放置更多晶体管实际上花费更多为什么,并且每美元可以添加的晶体管数量开始下降。

So, for the solution of above problem, 那么,对于上述问题的解决方案如下,

  • Manufacturers started adding more and more cores to the processor. Nowadays we have quad-core and octa-core CPUs available.
  • 制造商开始为处理器添加越来越多的内核。 如今我们有四核和八核CPU可用。
  • We also introduced hyper-threading.
  • 我们还介绍了超线程。
  • Added more cache to the processor to increase the performance.
  • 为处理器添加了更多缓存以提高性能。

But above solutions have its own limitations too. We cannot add more and more cache to the processor to increase performance as cache have physical limits: the bigger the cache, the slower it gets. Adding more core to the processor has its cost too. Also, that cannot scale to indefinitely. These multi-core processors can run multiple threads simultaneously and that brings concurrency to the picture. We’ll discuss it later.

但上述解决方案也有其自身的局限性。 我们无法向处理器添加越来越多的缓存以提高性能,因为缓存具有物理限制:缓存越大,获得的速度越慢。 为处理器添加更多核心也有其成本。 而且,这无法无限扩展。 这些多核处理器可以同时运行多个线程,从而为图像带来并发性。 我们稍后会讨论它。

So, if we cannot rely on the hardware improvements, the only way to go is more efficient software to increase the performance. But sadly, modern programming language are not much efficient. 因此,如果我们不能依赖硬件改进,唯一的出路就是提高软件的性能。 但遗憾的是,现代编程语言效率不高。

“Modern processors are a like nitro fueled funny cars, they excel at the quarter mile. Unfortunately modern programming languages are like Monte Carlo, they are full of twists and turns.” — David Ungar
“现代处理器就像硝基燃料有趣的汽车,他们擅长四分之一英里。 不幸的是,现代编程语言就像蒙特卡罗,它们充满了曲折。“ - David Ungar
Go has goroutines !!

As we discussed above, hardware manufacturers are adding more and more cores to the processors to increase the performance. All the data centers are running on those processors and we should expect increase in the number of cores in upcoming years. More to that, today’s applications using multiple micro-services for maintaining database connections, message queues and maintain caches. So, the software we develop and the programming languages should support concurrency easily and they should be scalable with increased number of cores.

如上所述,硬件制造商正在为处理器添加越来越多的内核以提高性能。 所有数据中心都在这些处理器上运行,我们预计未来几年核心数量会增加。 更重要的是,今天的应用程序使用多个微服务来维护数据库连接,消息队列和维护缓存。 因此,我们开发的软件和编程语言应该可以轻松地支持并发性,并且它们应该随着核心数量的增加而可扩展。

But, most of the modern programming languages(like Java, Python etc.) are from the ’90s single threaded environment. Most of those programming languages supports multi-threading. But the real problem comes with concurrent execution, threading-locking, race conditions and deadlocks. Those things make it hard to create a multi-threading application on those languages.

但是,大多数现代编程语言(如Java,Python等)都来自90年代的单线程环境。 大多数编程语言都支持多线程。 但真正的问题是并发执行,线程锁定,竞争条件和死锁。 这些事情使得很难在这些语言上创建多线程应用程序。

For an example, creating new thread in Java is not memory efficient. As every thread consumes approx 1MB of the memory heap size and eventually if you start spinning thousands of threads, they will put tremendous pressure on the heap and will cause shut down due to out of memory. Also, if you want to communicate between two or more threads, it’s very difficult. 例如,在Java中创建新线程不是内存有效的。 因为每个线程消耗大约1MB的内存堆大小,并且最终如果你开始旋转数千个线程,它们将对堆施加巨大的压力并且由于内存不足而导致关闭。 此外,如果您想在两个或多个线程之间进行通信,则非常困难。

On the other hand, Go was released in 2009 when multi-core processors were already available. That’s why Go is built with keeping concurrency in mind. Go has goroutines instead of threads. They consume almost 2KB memory from the heap. So, you can spin millions of goroutines at any time. 另一方面,Go于2009年发布,当时多核处理器已经上市。 这就是为什么Go是在保持并发性的基础上构建的。 Go有goroutines而不是线程。 它们从堆中消耗了大约2KB的内存。 因此,您可以随时旋转数百万个goroutine。 How Goroutines work? Reffrance: http://golangtutorials.blogspot.in/2011/06/goroutines.html

Other benefits are :

  • Goroutines have growable segmented stacks. That means they will use more memory only when needed.
  • Goroutines具有可增长的分段堆栈。 这意味着他们只在需要时才会使用更多内存。
  • Goroutines have a faster startup time than threads.
  • Goroutines的启动时间比线程快。
  • Goroutines come with built-in primitives to communicate safely between themselves (channels).
  • Goroutines带有内置的原语,可以在它们之间(通道)安全地进行通信。
  • Goroutines allow you to avoid having to resort to mutex locking when sharing data structures.
  • Goroutines允许您在共享数据结构时避免使用互斥锁。
  • Also, goroutines and OS threads do not have 1:1 mapping. A single goroutine can run on multiple threads. Goroutines are multiplexed into small number of OS threads.
  • 此外,goroutines和OS线程没有1:1映射。 单个goroutine可以在多个线程上运行。 Goroutines被多路复用到少量的OS线程中。
You can see Rob Pike’s excellent talk [concurrency is not parallelism](https://blog.golang.org/concurrency-is-not-parallelism) to get more deep understanding on this.

All the above points, make Go very powerful to handle concurrency like Java, C and C++ while keeping concurrency execution code strait and beautiful like Erlang. 以上几点,使Go非常强大,在处理并发操作的时候可以像Java,C和C ++一样高效,同时像Erlang一样美观。

Go takes good of both the worlds. Easy to write concurrent and efficient to manage concurrency Go擅长这两个世界。 易于编写并发和高效管理并发

#####Go runs directly on underlying hardware(Go直接在底层硬件上运行。). One most considerable benefit of using C, C++ over other modern higher level languages like Java/Python is their performance. Because C/C++ are compiled and not interpreted. 与其他现代高级语言(如Java / Python)相比,使用C,C ++的一个最大好处是它们的性能。 因为C / C ++是编译的而不是解释的。

Processors understand binaries. Generally, when you build an application using Java or other JVM-based languages when you compile your project, it compiles the human readable code to byte-code which can be understood by JVM or other virtual machines that run on top of underlying OS. While execution, VM interprets those bytecodes and convert them to the binaries that processors can understand. 处理器理解二进制文件。 通常,在编译项目时使用Java或其他基于JVM的语言构建应用程序时,它会将人类可读代码编译为字节代码,JVM或在底层操作系统之上运行的其他虚拟机可以理解这些代码。 执行时,VM会解释这些字节码并将其转换为处理器可以理解的二进制文件

Execution steps for VM based languages

While on the other side, C/C++ does not execute on VMs and that removes one step from the execution cycle and increases the performance. It directly compiles the human readable code to binaries. 而在另一方面,C / C ++不会在VM上执行,并且从执行周期中删除这一步并提高性能。 它直接将人类可读代码编译为二进制文件。

But, freeing and allocating variable in those languages is a huge pain. While most of the programming languages handle object allocation and removing using Garbage Collector or Reference Counting algorithms. 但是,在这些语言中释放和分配变量是一个巨大的痛苦。 虽然大多数编程语言使用垃圾收集器或引用计数算法处理对象的创建和释放。

Go brings best of both the worlds. Like lower level languages like C/C++, Go is compiled language. That means performance is almost nearer to lower level languages. It also uses garbage collection to allocation and removal of the object. So, no more malloc() and free() statements!!! Cool!!! Go带来了两全其美。 像C / C ++这样的低级语言一样,Go是编译语言。 这意味着性能几乎接近较低级别的语言。 它还使用垃圾收集来分配和删除对象。 所以,不再使用malloc()和free()语句! Cool!!!

#####Code written in Go is easy to maintain(用Go编写的代码易于维护). Let me tell you one thing. Go does not have crazy programming syntax like other languages have. It has very neat and clean syntax. 让我告诉你一件事。 Go没有像其他语言那样疯狂的编程语法。 它具有非常整洁和清晰的语法。

The designers of the Go at google had this thing in mind when they were creating the language. As google has the very large code-base and thousands of developers were working on that same code-base, code should be simple to understand for other developers and one segment of code should has minimum side effect on another segment of the code. That will make code easily maintainable and easy to modify. Go的设计者在谷歌创建这个语言时考虑到了这一点。 由于谷歌拥有非常庞大的代码库,成千上万的开发人员正在开发相同的代码库,因此对于其他开发人员来说代码应当是很容易理解的,而代码的一段代码应该对代码的另一部分产生最小的副作用。 这将使代码易于维护且易于修改。

Go intentionally leaves out many features of modern OOP languages. Go 故意遗漏了现代OOP语言的许多功能。

  • No classes. Every thing is divided into packages only. Go has only structs instead of classes.
  • 没有类。 一切都只分为包装。 Go只有结构而不是类。
  • Does not support inheritance. That will make code easy to modify. In other languages like Java/Python, if the class ABC inherits class XYZ and you make some changes in class XYZ, then that may produce some side effects in other classes that inherit XYZ. By removing inheritance, Go makes it easy to understand the code also (as there is no super class to look at while looking at a piece of code).
  • 不支持继承。 这将使代码易于修改。 在其他语言(如Java / Python)中,如果类ABC继承类XYZ并且您在类XYZ中进行了一些更改,那么这可能会在继承自XYZ的其他类中产生一些副作用。 通过删除继承,Go使理解代码更轻松(因为在查看一段代码时没有超级类可以查看)。
  • No constructors.
  • No annotations.
  • No generics.
  • No exceptions.

Above changes make Go very different from other languages and it makes programming in Go different from others. You may not like some points from above. But, it is not like you can not code your application without above features. All you have to do is write 2–3 more lines. But on the positive side, it will make your code cleaner and add more clarity to your code. 以上更改使Go与其他语言大不相同,使用Go编程的时候也与其他语言不同。 你可能不喜欢上面的一些观点。 但是,如果没有上述功能,您就无法对应用程序进行编码。 你所要做的就是再写2-3行。 但从积极的方面来说,它将使您的代码更清晰,并为您的代码添加更多清晰度。

Code readability vs, Efficiency.

Above graph displays that Go is almost as efficient as C/C++, while keeping the code syntax simple as Ruby, Python and other languages. That is a win-win situation for both humans and processors!!! 上图显示Go几乎与C / C ++一样高效,同时保持代码语法简单,如Ruby,Python和其他语言。 对于人类和处理器来说,这是一个双赢的局面!

Unlike other new languages like Swift, it’s syntax of Go is very stable. It remained same since the initial public release 1.0, back in year 2012. That makes it backward compatible. 与Swift等其他新语言不同,Go的语法非常稳定。 自从2012年首次公开发布1.0以来,它保持不变。这使其向后兼容。

Go is backed by Google.(Go由Google支持。)
  • I know this is not a direct technical advantage. But, Go is designed and supported by Google. Google has one of the largest cloud infrastructures in the world and it is scaled massively. Go is designed by Google to solve their problems of supporting scalability and effectiveness. Those are the same issues you will face while creating your own servers.
  • 我知道这不是直接的技术优势。 但是,Go是由Google设计和支持的。 谷歌拥有世界上最大的云基础设施之一,并且规模庞大。 Go由Google设计,旨在解决支持可扩展性和有效性的问题。 这些是您在创建自己的服务器时将面临的相同问题。
  • More to that Go is also used by some big companies like Adobe, BBC, IBM, Intel and even Medium.(Source: https://github.com/golang/go/wiki/GoUsers)
  • 更重要的是Go也被一些大公司使用,如Adobe,BBC,IBM,英特尔甚至是Medium。(来源:https://github.com/golang/go/wiki/GoUsers)
Conclusion(结论):
  • Even though Go is very different from other object-oriented languages, it is still the same beast. Go provides you high performance like C/C++, super efficient concurrency handling like Java and fun to code like Python/Perl.
  • 尽管Go与其他面向对象的语言非常不同,但它仍然是同一个野兽。 Go为您提供高性能,如C / C ++,超高效的并发处理,如Java,以及Python / Perl等代码的乐趣。
  • If you don’t have any plans to learn Go, I will still say hardware limit puts pressure to us, software developers to write super efficient code. Developer needs to understand the hardware and make their program optimize accordingly. The optimized software can run on cheaper and slower hardware (like IOT devices) and overall better impact on end user experience.
  • 如果你没有任何学习Go的计划,我仍然会说硬件限制会给我们带来压力,软件开发人员需要编写超高效的代码。 开发人员需要了解硬件并相应地优化其程序。 优化的软件可以在更便宜和更慢的硬件(如IOT设备)上运行,并且总体上对最终用户体验有更好的影响。

Tip

线上APP怎么搜集Crash日志

  1. 通过iOS SDK中提供的一个现成的函数 NSSetUncaughtExceptionHandler, NSSetUncaughtExceptionHandler用来做异常处理,用法如下:

     //异常回调方法
     void UncaughtExceptionHandler(NSException *exception) {
         NSArray *arr = [exception callStackSymbols];
         NSString *reason = [exception reason];
         NSString *name = [exception name];
         NSLog(@"%@\n%@\n%@",arr, reason, name);
     }
        
     - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
         // Override point for customization after application launch.
         NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);//注册异常回调方法
        
         NSArray *arr = @[@(0), @(1)];
         NSLog(@"%@", arr[2]); //模拟越界异常   
         return YES;
     }
    

    使用的时候有个地方需要注意一下就是,多个Crash日志收集服务共存的时候,比如工程中使用的第三方SDK可能自身集成的有Crash收集服务。当有多个Crash收集服务的时候,就会出现竞争的情况,导致有的Crash服务不能正常工作。 如果同时有多方通过NSSetUncaughtExceptionHandler注册异常处理程序,和平的做法是:后注册者通过NSGetUncaughtExceptionHandler将先前别人注册的handler取出并备份,在自己handler处理完后自觉把别人的handler注册回去,规规矩矩的传递。 如果不传递强行覆盖的后果是,在其之前注册过的日志收集服务写出的Crash日志就会因为取不到NSException而丢失Last Exception Backtrace等信息。(P.S. iOS系统自带的Crash Reporter不受影响) 在开发测试阶段,可以利用 fishhook 框架去hookNSSetUncaughtExceptionHandler方法,这样就可以清晰的看到handler的传递流程断在哪里,快速定位污染环境者。不推荐利用调试器添加符号断点来检查,原因是一些Crash收集框架在调试状态下是不工作的。

    检测代码示例:

     static NSUncaughtExceptionHandler *g_vaildUncaughtExceptionHandler;
     static void (*ori_NSSetUncaughtExceptionHandler)( NSUncaughtExceptionHandler * );
     void my_NSSetUncaughtExceptionHandler( NSUncaughtExceptionHandler * handler)
     {
         g_vaildUncaughtExceptionHandler = NSGetUncaughtExceptionHandler();
         if (g_vaildUncaughtExceptionHandler != NULL) {
             NSLog(@"UncaughtExceptionHandler=%p",g_vaildUncaughtExceptionHandler);
         }
             
         ori_NSSetUncaughtExceptionHandler(handler);
         NSLog(@"%@",[NSThread callStackSymbols]);
             
         g_vaildUncaughtExceptionHandler = NSGetUncaughtExceptionHandler();
         NSLog(@"UncaughtExceptionHandler=%p",g_vaildUncaughtExceptionHandler);
     }
    

    上面一段话摘自–漫谈iOS Crash收集框架:http://www.cocoachina.com/ios/20150701/12301.html

  2. 使用signal,NSSetUncaughtExceptionHandler不能处理所有的异常,对于类似EXC_BAD_ACCESS引起的崩溃,NSSetUncaughtExceptionHandler就无能为力了,这时就需要使用signal来处理。 什么是Signal?

    在计算机科学中,信号(英语:Signals)是Unix、类Unix以及其他POSIX兼容的操作系统中进程间通讯的一种有限制的方式。它是一种异步的通知机制,用来提醒进程一个事件已经发生。当一个信号发送给一个进程,操作系统中断了进程正常的控制流程,此时,任何非原子操作都将被中断。如果进程定义了信号的处理函数,那么它将被执行,否则就执行默认的处理函数。

    使用方法和上面一样,也是通过注册回调的方法实现的 1 注册

     void InstallUncaughtExceptionHandler(void){
        
         //设置信号类型的异常处理
         signal(SIGABRT, HandleSignal);
         signal(SIGILL, HandleSignal);
         signal(SIGSEGV, HandleSignal);
         signal(SIGFPE, HandleSignal);
         signal(SIGBUS, HandleSignal);
         signal(SIGPIPE, HandleSignal);
     }
        
     void HandleSignal(int signal){
         int32_t exceptionCount= OSAtomicIncrement32(&exceptionCount);
         if (exceptionCount>exceptionMaximum) {
             return;
         }
            
         NSMutableDictionary *userInfo=[NSMutableDictionary dictionaryWithObject:[NSNumber numberWithInt:signal] forKey:UncaughtExceptionHandlerSignalKey];
         NSArray *callBack=[UncaughtExceptionHandler backtrace];
         [userInfo setObject:callBack forKey:UncaughtExceptionHandlerAddressesKey];
            
         UncaughtExceptionHandler *uncaughtExceptionHandler=[[UncaughtExceptionHandler alloc] init];
         NSException *signalException=[NSException exceptionWithName:UncaughtExceptionHandlerSignalExceptionName reason:[NSString stringWithFormat:@"Signal %d was raised.",signal] userInfo:userInfo];
         [uncaughtExceptionHandler performSelectorOnMainThread:@selector(handleException:) withObject:signalException waitUntilDone:YES];
     }
    

    具体参见代码:https://github.com/dandan2009/Signal

    iOS Crash收集是一个大的话题,本文算是初步入门,要想深入需要了解操作系统等知识。待以后深入研究吧。

    参考: 漫谈iOS Crash收集框架:http://www.cocoachina.com/ios/20150701/12301.html iOS崩溃crash大解析:https://www.jianshu.com/p/1b804426d212、http://www.qidiandasheng.com/2016/04/10/crash-xuebeng/ iOS应用崩溃日志分析:http://www.cocoachina.com/industry/20130725/6677.html iOS异常捕获:http://www.iosxxx.com/blog/2015-08-29-iosyi-chang-bu-huo.html 使用signal让app能够在从容崩溃:https://www.cnblogs.com/daxiaxiaohao/p/4466097.html https://github.com/walkdianzi/DSSignalHandlerDemo

Share

分享一下,对于四级没过,英语基础不好,又想提高英语的同学的一些英语学习方法:(以下很大内容都是摘抄,详细内容请点击对应的链接)

  1. 学习英语APP: 其实到App Store搜索关键字 英语、英语阅读 有很多APP,有的是收费的,有的免费的,根据自己的需求选择。

  2. 英语学习网站 https://www.rong-chang.com/ http://www.bbc.co.uk/learningenglish/ https://dictionary.cambridge.org/ http://www.dictionary.com/ 英文科技文章: https://medium.com/

  3. 发音练习: 喜马拉雅 赖世雄的音标课程

  4. 左耳朵耗子–陈皓的建议(具体参见https://time.geekbang.org/column/intro/48 陈皓的课程): 1 坚持 Google 英文关键词,而不是在 Google 里搜中文。 2 在 GitHub 上只用英文。用英文写代码注释,写 Code Commit 信息,用英文写 Issue 和 Pull Request,以及用英文写 Wiki。 3 坚持到 YouTube 上每天看 5 分钟的视频。YouTube 上有相关的机器字幕,实在不行就打开字幕。 4 坚持用英文词典而不是中文的。比如:剑桥英语词典(https://dictionary.cambridge.org/) 或是 Dictionary.com(http://www.dictionary.com/) 。你可以安装一个 Chrome 插件 Google Dictionary(https://chrome.google.com/webstore/detail/google-dictionary-by-goog/mgijmajocgfcbeboacabfgobmjgjcoja)。 5 坚持用英文的教材而不是中文的。比如:BBC 的 Learning English(http://www.bbc.co.uk/learningenglish/) ,或是到一些 ESL 网站上看看,如 ESL: English as a Second Language(https://www.rong-chang.com/ ) 上有一些课程。 6 花钱参加一些线上的英文课程,用视频和老外练习。

  5. tinyfool的我是怎么学英语的(四级没过如何突破听说读写)(https://mp.weixin.qq.com/s?__biz=MjM5MjUwNzIyMA==&mid=207623278&idx=1&sn=051a2ecae8f0392631eb0967eefc607a#rd)摘录: 阅读文档根本不需要你英文好到我这个水平 查字典就可以了,技术文档来回来去就几百个词,根本不需要背,每次看不懂就查 看一个星期基本上就都会了 这个方法说了无数遍了,不肯试的人一辈子做技术英语文盲

    我英语4级都没过……反正看文档,遇到单词不会就谷歌翻译一下…..查个3,5遍API你想记不住都不行…..技术单词也就那么几个…..

    1、硬学。看不懂的美剧硬看,听不懂的Podcast硬听,看不懂的书硬看,聊不清楚的老外硬聊,写不好的英文文章硬写。

    2、循序渐进。虽然是硬学,一开始绝对由浅入深,让自己时刻都有成就感。

    3、追求最大的材料量。循序渐进的目的是一直都没有挫折感,所以可以用大量的时间去看美剧(至少上千小时),去听Podcast(几百小时了),去跟老外扯淡,去写文章。

    4、逐步改进,慢慢地从学语言本身,进化到学文化,学跟世界交流。这个过程越深入,学习的动力越足。

    5、不急躁不冒进,日拱一卒,不期速成。看美剧突破用了半年,但是一直看到现在。听Podcast突破用了一个月。口语用了几个月,写作用了几个月。听起来很浪费时间,但是几年以后,想对自己的能力提升去看,觉得自己花的时间并不多,很少,很值得。

    6、保持快乐,所以可以坚持做终身学习。我的英语水平高么,比以前高了很多,足够高么,不足够高,但是我可以自傲的是,现在我的英语学习是终身学习,你比我高,没关系,大多数人的学习速度没我快,而且不像我不停的在学习,总有一天超过你。(当然最重要的永远是追求每日超越昨天的自己。)

  6. 来自知乎的问答: 但是最后发现,最有效的方法其实就是最笨的方法——多读,多听,多记笔记,多写,总结,反复练习……

    其实没有那么多学英语的方法。

    你仔细想想,很多时候,所谓「最高效」的学习方法往往才是最低效的。因为,它们只是给了你短期的满足感。

    而长期的进步,关键在于你是不是能坚持,坚持就是反复,反复,在反复的做同一件事情。

    而且我逐渐的意识到,

    我永远也学不会英语,

    因为永远有我不知道的单词和用法,

    但是我愿意保持学习,直到最后一天。

    因为终身坚持学习,才是一个人最cool的生活方式。

    ======== https://www.zhihu.com/question/19853667/answer/134793017 我的方法很简单很无脑,对于那些真正热爱和真正想学好英语的同学应该管用。

    每当我看到别人一副认真背单词的样子,我就觉得这个人肯定学不好英语,他背单词背的越多,背的越认真,我觉得他就离英语高手的目标就越远。

    记得上大学时总是听到有同学在走廊里大声的背诵的“abandon,abandon,a、b、a、n、d、o、

    n,a、b、a、n、d、o、n”一遍又一遍,越背越有激情,从单词书的第一页开始背诵,一晚上能背好几页,学习激情万丈,不可谓不用功,估计晚上回去睡一觉第二天就全部忘了。

    如果是一个小学生或者中学生这样背单词学英语,我觉得无可厚非,如果一个要考六级的大学生这样背单词备考,我真想过去抽走他的书,给他两个嘴巴子,这他妈是学英语吗?后来我看了一下背单词的那些人四肢都比较发达,我就放弃了这个念头。

    不过我想说,你他吗要是按照这种学习方法学英语的话,那学汉语应该是这么学的,从生字表第一页开始应该是这么背的:

    “一,一,横,横,横。”

    “二,二,横横,横横,横横。”

    “三,三,横横横,横横横,横横横。”

    “四,四,竖、横折、左撇、右撇、横,竖、横折、左撇、右撇、横,竖、横折、左撇、右撇、横。”

    。。。。。。

    你他妈要是背到陕西的“biang biang 面”的时候我估计你就死机了。

    看看biang字怎么写吧! 学英语真的不是这么学到,没见过那个人这样学汉语的,但是倒是很多人这么学英语,虽然说英语和汉语不一样,但是道理是相通的。

    那到底应该怎么学呢?

    其实你可以想象一下你是怎么学汉语的,你就可以借鉴过来学英语。

    我们小时候学写汉字,写生字,认字,这可以对比成背单词,但是这都是在你小学时候干的事情,你学习语言之初你可以这么干,因为你这个阶段基础很差,最常见的单词都不认识那就需要写生字背单词,但是你都上了高中了你学习语文还从小学课本的生字表开始写生字是一种什么概念,你上了大学要考六级了你学习英语的大部分时间用来背单词我就不信你能学好英语。

    高中的时候我们怎么学语文呢?

    阅读、阅读、再阅读,用英语说就是reading ,reading,and reading,写作,写作,再写作,用英语说就是writing,writing,and writing。

    肯定有人会反驳我说,我都不认识单词怎么reading啊?

    你可以找一些适合你阅读的文章进行大量的阅读,阅读过程中肯定会碰到很多单词不认识,你在语文阅读中也会碰到不认识的字或者不理解的词语,这很正常,怎么办,你去查字典搞清楚就行了,然后继续阅读,直到随着你的阅读量越来越大,你会发现这个档次的文章需要你查询的单词越来越少了,然后就换下一个高档次的文章来阅读,如此做下去就是了,当你的英语阅读量达到一定的水平以后,你的单词量自然就上去了。

    明白我说的意思了没有?不要背单词,无聊的要命还记不住,大量的阅读,疯狂的阅读,当你的阅读量达到一定水平的时候除了听力和口语你的英语就算过关了。

    我怕我有一天忘了使用英语阅读我的英语就会退化,所以我的手机从来都是英语模式的。有时候别人想用一下我的手机发现都用不了。

    学习英语就是这么简单,就是这么无脑。 https://www.zhihu.com/question/26677313/answer/230847636?group_id=892423340218806272

=======

以上内容很多都是摘抄,详细内容请点击对应的链接: 对上面的方法做个总结,就是不要害怕英语,要硬着头皮学,这里硬着头皮学的意思是遇到英文文章要敢读,不能放弃,遇到不会的单词要积极的查词典,而不是放弃,不能痛苦,一旦你觉得学习英语是件痛苦的事的时候,你就不要学了。真的,这个时候效果肯定很差。

其实我高考时候的英语经历过从低分30多分到100多分的经历,那时是怎么提高的呢,实际上就是阅读,阅读的时候遇到不会的单词就查字典,然后记忆,通过阅读文章记单词要比单独记单词效果要好的多,如果基础不好,可以先从简单的文章入手,然后慢慢加深难度,对于技术文章,实际上借助谷歌翻译大部分都可以读懂,就看你能不能坚持,只要能坚持个半年,每周读三篇英文文章,我想英文的能力也就上去了。

=======