멋진 기능 코드를 읽고 작성하는 방법
오늘 웨이보에서 다음과 같은 기능 코드를 공유하는 것을 보았습니다. 아래 코드를 게시했는데, 기능 버전의 경우 언뜻 보면 이해하기가 매우 어렵습니다. 자세히 보면 기절할 수도 있어요. 완전 하늘에서 내려온 책인 것 같아요. 하하. 하지만 그 기능적 코드를 파싱하는 것이 더 흥미로운 과정이 될 수 있다고 생각합니다. 게다가 이전에 "함수형 프로그래밍"에 대한 소개 기사를 작성한 적이 있는데, 이 예제를 사용하여 원본 기사를 승화시킬 수 있습니다. 많은 기초 지식을 여러분에게 소개하는 것이 좋을 것 같아서 이 글을 썼습니다.
코드를 먼저 보세요
이 코드는 특이하지 않은 배열에서 숫자를 찾는 것입니다. 찾을 수 없으면 null을 반환하세요.
다음은 일반적인 구식 방식입니다. 말할 필요도 없습니다.
//正常的版本 function find (x, y) { for ( let i = 0; i < x.length; i++ ) { if ( x[i] == y ) return i; } return null; } let arr = [0,1,2,3,4,5] console.log(find(arr, 2)) console.log(find(arr, 8))
결과적으로 함수식은 이런 모습이 됩니다. (위의 코드들이 아래에 희미하게 보이는 것 같지만 조금 다릅니다. if 언어를 없애기 위해, it be visible ? 표현식을 사용하면 표현식에 더 가깝습니다.
//函数式的版本 const find = ( f => f(f) ) ( f => (next => (x, y, i = 0) => ( i >= x.length) ? null : ( x[i] == y ) ? i : next(x, y, i+1))((...args) => (f(f))(...args))) let arr = [0,1,2,3,4,5] console.log(find(arr, 2)) console.log(find(arr, 8))
이 코드를 명확하게 설명하려면 먼저 몇 가지 지식을 추가해야 합니다.
Javascript의 화살표 함수
먼저 ECMAScript2015에서 도입한 화살표 표현식에 대해 간단히 설명하겠습니다. 화살표 함수는 실제로 익명 함수이며 기본 구문은 다음과 같습니다.
(param1, param2, …, paramN) => { statements } (param1, param2, …, paramN) => expression // 等于 : => { return expression; } // 只有一个参数时,括号才可以不加: (singleParam) => { statements } singleParam => { statements } //如果没有参数,就一定要加括号: () => { statements }
다음은 몇 가지 예입니다.
var simple = a => a > 15 ? 15 : a; simple(16); // 15 simple(10); // 10 let max = (a, b) => a > b ? a : b; // Easy array filtering, mapping, ... var arr = [5, 6, 13, 0, 1, 18, 23]; var sum = arr.reduce((a, b) => a + b); // 66 var even = arr.filter(v => v % 2 == 0); // [6, 0, 18] var double = arr.map(v => v * 2); // [10, 12, 26, 0, 2, 36, 46]
복잡해 보이지 않습니다. . 그러나 위의 처음 두 개의 단순 예제와 최대 예제는 모두 화살표 함수를 변수에 할당하므로 이름이 있습니다. 때로는 특정 함수가 선언될 때, 특히 함수형 프로그래밍에서 함수가 외부 함수도 반환할 때 호출됩니다. 예를 들어, 다음 예에서는
function MakePowerFn(power) { return function PowerFn(base) { return Math.pow(base, power); } } power3 = MakePowerFn(3); //制造一个X的3次方的函数 power2 = MakePowerFn(2); //制造一个X的2次方的函数 console.log(power3(10)); //10的3次方 = 1000 console.log(power2(10)); //10的2次方 = 100
실제로 MakePowerFn 함수의 PowerFn에는 이름을 지정할 필요가 전혀 없습니다.
<로 작성할 수 있습니다. 🎜>
function MakePowerFn(power) { return function(base) { return Math.pow(base, power); } }
MakePowerFn = power => { return base => { return Math.pow(base, power); } }
MakePowerFn => base => Math.pow(base, power)
더 명확하게 하기 위해 괄호와 줄바꿈을 추가하겠습니다.
MakePowerFn = (power) => ( (base) => (Math.pow(base, power)) )
익명 함수의 재귀
함수 프로그래밍은 상태 저장 함수와 함수 표현식이 있는 for/while 루프를 제거하는 것을 목표로 하므로 함수에서는 형식 프로그래밍의 세계에서 , for/while 루프는 사용하지 말고 대신 재귀를 사용해야 함(재귀 성능이 매우 좋지 않아 일반적으로 최적화를 위해 꼬리 재귀를 사용함, 즉 함수의 계산된 상태를 매개변수로 사용함) 합격 언어 컴파일러나 해석기가 함수의 내부 변수 상태를 저장하는 데 도움을 주기 위해 함수 스택을 사용할 필요가 없도록 계층별로 계층화합니다.
그럼 익명함수 재귀는 어떻게 할까요?
일반적으로 재귀 코드는 함수가 자신을 호출하는 경우입니다. 예를 들어 계승을 찾는 코드는 다음과 같습니다.
function fact(n){ return n==0 ? 1 : n * fact(n-1); }; result = fact(5);
function combinator(func) { func(func); }
(func) => (func(func))
이제 바람피울 것 같지 않네요. 위의 계승 함수를 삽입하는 방법은 다음과 같습니다.
먼저 사실을 재구성하고 사실에서 자신이라고 부르는 이름을 제거합니다.
function fact(func, n) { return n==0 ? 1 : n * func(func, n-1); } fact(fact, 5); //输出120
fact(fact, 5)
즉,
(func, n) => ( n==0 ? 1 : n * func(func, n-1) )
(func, x) => func(func, x)
마침내 다음 코드를 얻습니다.
( (func, x) => func(func, x) ) ( //函数体 (func, n) => ( n==0 ? 1 : n * func(func, n-1) ), //第一个调用参数 5 //第二调用参数 );
고차 함수를 이용한 재귀
그런데 위 재귀 익명함수는 자기 자신을 호출하기 때문에 코드에 실제 하드코드의 매개변수가 들어있습니다. 실제 매개변수를 제거하고 싶은데 어떻게 제거하나요? 앞서 언급한 MakePowerFn 예제를 참조할 수 있지만 이번에는 고차 함수의 재귀 버전입니다.
HighOrderFact = function(func){ return function(n){ return n==0 ? 1 : n * func(func)(n-1); }; };
fact = HighOrderFact(HighOrderFact); fact(5);
连起来写就是:
HighOrderFact ( HighOrderFact ) ( 5 )
但是,这样让用户来调用很不爽,所以,以我们一个函数把 HighOrderFact ( HighOrderFact ) 给代理一下:
fact = function ( hifunc ) { return hifunc ( hifunc ); } ( //调用参数是一个函数 function (func) { return function(n){ return n==0 ? 1 : n * func(func)(n-1); }; } ); fact(5); //于是我们就可以直接使用了
用箭头函数重构一下,是不是简洁了一些?
fact = (highfunc => highfunc ( highfunc ) ) ( func => n => n==0 ? 1 : n * func(func)(n-1) );
上面就是我们最终版的阶乘的函数式代码。
回顾之前的程序
我们再来看那个查找数组的正常程序:
//正常的版本 function find (x, y) { for ( let i = 0; i < x.length; i++ ) { if ( x[i] == y ) return i; } return null; }
先把for干掉,搞成递归版本:
function find (x, y, i=0) { if ( i >= x.length ) return null; if ( x[i] == y ) return i; return find(x, y, i+1); }
然后,写出带实参的匿名函数的版本(注:其中的if代码被重构成了 ?号表达式):
( (func, x, y, i) => func(func, x, y, i) ) ( //函数体 (func, x, y, i=0) => ( i >= x.length ? null : x[i] == y ? i : func (func, x, y, i+1) ), //第一个调用参数 arr, //第二调用参数 2 //第三调用参数 )
最后,引入高阶函数,去除实参:
const find = ( highfunc => highfunc( highfunc ) ) ( func => (x, y, i = 0) => ( i >= x.length ? null : x[i] == y ? i : func (func) (x, y, i+1) ) );
注:函数式编程装逼时一定要用const字符,这表示我写的函数里的状态是 immutable 的,天生骄傲!
再注:我写的这个比原来版的那个简单了很多,原来版本的那个又在函数中套了一套 next, 而且还动用了不定参数,当然,如果你想装逼装到天上的,理论上来说,你可以套N层,呵呵。
现在,你可以体会到,如此逼装的是怎么来的了吧?。
其它
你还别说这就是装逼,简单来说,我们可以使用数学的方式来完成对复杂问题的描述,那怕是递归。其实,这并不是新鲜的东西,这是Alonzo Church 和 Haskell Curry 上世纪30年代提出来的东西,这个就是 Y Combinator 的玩法,关于这个东西,你可以看看下面两篇文章:《The Y Combinator (Slight Return)》,《Wikipedia: Fixed-point combinator》

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제









