Former article: juejin. Cn/post / 693826…

Day 1: Start coding again

So here we go. Remember the convention of the function?

Student: Remember, data is immutable.

Fang: Ok, so I guess you can’t write code now.

Student:?

Fang: Don’t believe me? Let me challenge you. Print the value of each item by traversing array = [‘a’,’b’,’c’]. Let me write it in JS.

Student: Easy

for(let i = 0; i< array.length; i++){
  console.log(array[i])
}
Copy the code

Fang: You forgot the appointment so soon? Data is immutable!

Student: I didn’t change array

Fang: I ++ changed the value of I

Student: Ah, that counts

Fang: That’s right. Instead of i++, you answer it again

Student: So I use “for in”

for(let key in array){
  console.log(array[key])
}
Copy the code

The key is still changing. The key is equal to 0 at the beginning, 1 at the end, and 2 at the end. Think again.

Student: I have another trick:

array.forEach(item => console.log(item))
Copy the code

Fang: I finally got to the edge, this time “you” did not change the value of the data

Student: So am I right?

Fang: Almost. Array. forEach is a built-in array interface for JAVASCRIPT. Can you write your own forEach? Satisfies the following usages

forEach(array, item => console.log(item))
Copy the code

Student: Don’t use array forEach to write your own forEach…

Fang: well

Student: Can you use while

F: no, while i++ is the same as for

Student: Recursion?

Fang: try

Student: yes

let array = ['a','b','c']
let forEach = (array, fn) => {
  if(array.length === 0) return
  fn(array[0])
  forEach(array.slice(1), fn)
}
forEach(array, item => console.log(item))
Copy the code

Fong: Yes, this time you kept your promise: “Data is immutable”. I wonder why you gave this notation at the end?

Student: I was told to try not to use recursion.

Fang: Let me guess, that person writes JS or Java?

Student: Well, more than one person said that.

Fang: What are the reasons they give?

Student: Easy to “stack overflow”, and “expensive” “waste memory”

Fang: That’s a good point, but that’s just one side of their story. Let’s talk about stack overflow first. Stack overflow requires a “callstack”, right?

Student: Sure

Fang: Did you know that some languages don’t have a Callstack at all? Haskell, for example, doesn’t have a callstack. It uses graphs instead of stacks.

Student: Never heard of it…

Fang: In addition, if we rewrite recursion to tail recursion, or even loop, we can basically eliminate stack overflow.

Student: But if you write recursion as a loop, why not just write a loop?

Fong: you are very keen to find the loophole in my logic, the “tail recursive change loop” here is automatically implemented by the “compiler”! That means programmers focus on writing code and compilers focus on performance optimization.

Student: What is tail recursion?

Fang: I’ll talk about that tomorrow. Today I’ll overturn your mistrust of recursion. Now all you need to know is that by rewriting recursion to tail recursion, with the right compiler, stack overflow is almost no problem.

Student: Wouldn’t a JS or Java compiler work?

Fang: Good question. In fact, compiler and programmer writing is complementary, not constant. Let me ask you first

  1. Programmers should remember code that performs well, even if readability is abandoned
  2. Programmers prioritize readability, leaving the compiler to find ways to optimize performance

Which of these two ideas do you support?

Student: I’m not sure, shouldn’t you try to pick the best code?

Fang: Haha, do you think that good code is good?

Student: What does that mean?

Fang: Let me give you an example. You know that ++ I is much better than I ++, right?

Student: Oh? Why is that?

Fang: The reason doesn’t matter. You can read this article or not. Assuming you already know that ++ I is much better than I ++, would you rather write I ++ or ++ I when you write a for loop?

Student: I used to write I ++. Now that you say so, should I write ++ I

If the third statement in the for loop is I ++, the compiler will automatically optimize to ++ I, so you don’t need to memorize so many rules. There are so many optimization tricks on the web that you can memorize a few

Student: The compiler is so smart!

Fang: You don’t want to think that the people who write compilers are much more intelligent than the people who write JS.

Student: Yeah

Fang: There are even times when the quirks you use to improve performance will degrade your program because it won’t be optimized by the compiler. The compiler generally optimizes common writing.

Student: I see

Fang: So programmers should try to write code that conforms to community conventions, and only manually optimize performance at performance bottlenecks. However, due to years of “education” by JS and Java people, newcomers like you have learned to avoid “recursion” as much as possible, so JS and Java compilers don’t need to spend time optimizing the minority writing, which can cause security and debug related problems.

Student: Is recursion “expensive” and “memory wasting” similar misconceptions?

Fang: Yes, if a language feature is used a lot by programmers, the compiler will try to make it faster. Now, can you give up your bias against recursion?

Student: Yes, do I have to give up the JS and Java compilers?

Fang: Yes, the community culture of the two languages is not very compatible with functions, and the major versions of the compiler do not support these optimizations, maybe a future version will.

Student: Let me ask again, “recursion” has no disadvantages?

Fang: Yes, I will talk about it later, but the flaws do not outweigh the disadvantages.

Student: Ok, I’ll write that down, although I’m not completely convinced yet.

Fang: Where did we get here? Oh, from “You can’t write recursion in the first place.” So I give you the second problem, write a function to reverse the string, can not violate the “data immutable” convention oh.

Student: I can write recursively

reverse = (string) => { if(string.length <= 1){return string} let last = string[string.length-1] let head = String. Substr (0, string.length-1) return last + reverse(head)Copy the code

Fang: It’s quite fast

Student: Sure enough, I wrote much faster after I dropped my prejudice, but I still felt that efficiency would be slow and memory would be wasted

Fang: You’ll get used to it in a few days, and I’ll show you how to optimize it. Let’s do one more, quicksort

Student: Easy

/*1*/ quickSort = (array) => {
/*2*/   if(array.length <= 1) {return array}
/*3*/   let [pivot, ...rest] = array
/*4*/   let small = rest.filter(i => i<=pivot)
/*5*/   let big = rest.filter(i => i>pivot)
/*6*/   return [...quickSort(small), pivot, ...quickSort(big) ]
/*7*/ }
Copy the code

Student: This is really cool, but I’m still worried about wasting memory. Lines 3, 4, 5, and 6 are all memory copies

Fong: Well, that’s because JS and Java data is mutable, so you can’t reuse data directly. If the data is immutable, let [pivot,…rest] = array can reuse memory directly

Student: It seems so, what about rest.filter(I => I <=pivot)

Fang: This memory is difficult to optimize. But you know, if you’re writing functional, you’re writing five lines of code, and you’re writing instruction, you’re writing 20 lines of code, and functional is all about logical brevity, making the logic nice, and then optimizing it when you hit a performance bottleneck

Student: Ok

Fang: Wait, how come you still don’t seem to fully accept “data immutable” and always want to change the original memory?

Student: It’s only the first day. I’ll get used to it in a few days

All right, let’s stop here for today and continue tomorrow.

Follow-up: juejin. Cn/post / 693919…