실용적인 방법
"올바르지 않은"솔루션과 달리 "올바른"( "올바른") 경우 특정 구현이 "The Right Way ™"라고 말하는 것이 잘못이라고 생각합니다. Tomáš의 솔루션은 문자열 기반 배열 비교에 비해 명백히 개선 된 것이지만 이것이 객관적으로 "옳다"는 것을 의미하지는 않습니다. 무엇 권리 어쨌든은? 가장 빠릅니까? 가장 유연합니까? 이해하기가 가장 쉬운가요? 디버깅하는 것이 가장 빠릅니까? 가장 적은 작업을 사용합니까? 부작용이 있습니까? 어떤 솔루션도 모든 것을 최대한 활용할 수는 없습니다.
Tomáš는 그의 솔루션이 빠르다고 말할 수 있지만 필연적으로 복잡하다고 말합니다. 중첩 여부에 관계없이 모든 어레이에서 작동하는 올인원 솔루션이 되려고합니다. 실제로, 그것은 단지 배열 이상의 것을 입력으로 받아들이고 여전히 "유효한"대답을 시도합니다.
일반은 재사용 성을 제공합니다
내 대답은 문제에 다르게 접근합니다. arrayCompare
배열을 통한 스테핑에만 관련된 일반적인 절차 부터 시작하겠습니다 . 여기에서 arrayEqual
and arrayDeepEqual
등 의 다른 기본 비교 함수를 작성합니다.
// arrayCompare :: (a -> a -> Bool) -> [a] -> [a] -> Bool
const arrayCompare = f => ([x,...xs]) => ([y,...ys]) =>
x === undefined && y === undefined
? true
: Boolean (f (x) (y)) && arrayCompare (f) (xs) (ys)
내 의견으로는, 가장 좋은 종류의 코드는 주석이 필요하지 않으며 예외는 아닙니다. 거의 아무런 노력 없이이 절차의 동작을 이해할 수 있도록 여기에는 거의 일어나지 않습니다. 물론, ES6 구문 중 일부는 현재 여러분에게 이질적으로 보이지만 ES6이 비교적 새롭기 때문입니다.
형식에서 알 수 있듯이 arrayCompare
비교 함수, f
두 개의 입력 배열 xs
및을 사용 ys
합니다. 대부분의 f (x) (y)
경우 입력 배열의 각 요소를 호출 하기 만하면됩니다. 단락 평가 덕분에 false
사용자가 정의한 f
반품 일 경우 조기에 반품합니다 . 따라서, 이것은 비교기가 반복을 조기에 중단하고 불필요 할 때 나머지 입력 배열을 통한 루핑을 방지 할 수 있음을 의미합니다.false
&&
엄격한 비교
다음으로 arrayCompare
함수를 사용하여 필요한 다른 함수를 쉽게 만들 수 있습니다. 우리는 초등학교에서 시작합니다 arrayEqual
...
// equal :: a -> a -> Bool
const equal = x => y =>
x === y // notice: triple equal
// arrayEqual :: [a] -> [a] -> Bool
const arrayEqual =
arrayCompare (equal)
const xs = [1,2,3]
const ys = [1,2,3]
console.log (arrayEqual (xs) (ys)) //=> true
// (1 === 1) && (2 === 2) && (3 === 3) //=> true
const zs = ['1','2','3']
console.log (arrayEqual (xs) (zs)) //=> false
// (1 === '1') //=> false
그렇게 간단합니다. (엄격한 평등을 위해) 를 사용 하는 것과 비교하는 비교기 함수 arrayEqual
로 정의 할 수 있습니다 .arrayCompare
a
b
===
우리는 또한 equal
자신의 기능으로 정의 합니다. 이것은 arrayCompare
다른 데이터 유형 (배열)의 맥락에서 첫 번째 비교기를 활용하는 고차 함수 의 역할을 강조합니다 .
느슨한 비교
우리는 그냥 간단하게 정의 할 수 arrayLooseEqual
사용하는 ==
대신합니다. 이제 1
(Number)를'1'
(String) 하면 결과는 다음과 같습니다 true
.
// looseEqual :: a -> a -> Bool
const looseEqual = x => y =>
x == y // notice: double equal
// arrayLooseEqual :: [a] -> [a] -> Bool
const arrayLooseEqual =
arrayCompare (looseEqual)
const xs = [1,2,3]
const ys = ['1','2','3']
console.log (arrayLooseEqual (xs) (ys)) //=> true
// (1 == '1') && (2 == '2') && (3 == '3') //=> true
깊은 비교 (재귀)
아마도 이것은 단지 얕은 비교 방법이라는 것을 알았을 것입니다. Tomáš의 솔루션은 "The Right Way ™"입니다. 암묵적인 심층 비교를하기 때문입니다.
글쎄, 우리의 arrayCompare
절차는 깊은 평등 테스트를 산들 바람으로 만드는 방법으로 사용할 수있을 정도로 다재다능합니다…
// isArray :: a -> Bool
const isArray =
Array.isArray
// arrayDeepCompare :: (a -> a -> Bool) -> [a] -> [a] -> Bool
const arrayDeepCompare = f =>
arrayCompare (a => b =>
isArray (a) && isArray (b)
? arrayDeepCompare (f) (a) (b)
: f (a) (b))
const xs = [1,[2,[3]]]
const ys = [1,[2,['3']]]
console.log (arrayDeepCompare (equal) (xs) (ys)) //=> false
// (1 === 1) && (2 === 2) && (3 === '3') //=> false
console.log (arrayDeepCompare (looseEqual) (xs) (ys)) //=> true
// (1 == 1) && (2 == 2) && (3 == '3') //=> true
그렇게 간단합니다. 우리는 또 다른 고차 함수를 사용하여 깊은 비교기를 만듭니다. 이번에 는 배열 과 배열 arrayCompare
인지 확인하는 사용자 지정 비교기를 사용하여 줄 바꿈 합니다. 그렇다면 다른 방법으로 다시 비교 하고 사용자 지정 비교기 ( )와 비교 하십시오. 이를 통해 우리는 개별 요소를 실제로 비교하는 방법과 깊은 비교 동작을 유지할 수 있습니다. 즉, 위의 예에서 볼 수 있듯이a
b
arrayDeepCompare
a
b
f
equal
, looseEqual
또는 기타 비교기는 우리가합니다.
arrayDeepCompare
카레 이기 때문에 이전 예제에서와 마찬가지로 부분적으로 적용 할 수 있습니다.
// arrayDeepEqual :: [a] -> [a] -> Bool
const arrayDeepEqual =
arrayDeepCompare (equal)
// arrayDeepLooseEqual :: [a] -> [a] -> Bool
const arrayDeepLooseEqual =
arrayDeepCompare (looseEqual)
필자는 필요에 따라 어레이에 대해 얕거나 깊은 비교를 명시 적으로 선택할 수 있기 때문에 Tomáš 솔루션보다 이미 분명히 개선되었습니다 .
객체 비교 (예)
이제 객체 배열이나 무언가가 있다면 어떨까요? 각 객체의 id
값 이 동일한 경우 해당 배열을 "동일"한 것으로 간주하고 싶을 수도 있습니다 .
// idEqual :: {id: Number} -> {id: Number} -> Bool
const idEqual = x => y =>
x.id !== undefined && x.id === y.id
// arrayIdEqual :: [a] -> [a] -> Bool
const arrayIdEqual =
arrayCompare (idEqual)
const xs = [{id:1}, {id:2}]
const ys = [{id:1}, {id:2}]
console.log (arrayIdEqual (xs) (ys)) //=> true
// (1 === 1) && (2 === 2) //=> true
const zs = [{id:1}, {id:6}]
console.log (arrayIdEqual (xs) (zs)) //=> false
// (1 === 1) && (2 === 6) //=> false
그렇게 간단합니다. 여기서는 바닐라 JS 객체를 사용했지만이 유형의 비교기는 모든 객체 유형에서 작동 할 수 있습니다 . 심지어 커스텀 객체까지. 이러한 종류의 동등성 테스트를 지원하기 위해 Tomáš의 솔루션을 완전히 재 작업해야합니다.
객체가있는 깊은 배열? 문제가 아니다. 우리는 매우 다양한 범용 기능을 구축하여 다양한 사용 사례에서 작동합니다.
const xs = [{id:1}, [{id:2}]]
const ys = [{id:1}, [{id:2}]]
console.log (arrayCompare (idEqual) (xs) (ys)) //=> false
console.log (arrayDeepCompare (idEqual) (xs) (ys)) //=> true
임의 비교 (예)
아니면 다른 종류의 완전히 임의적 인 비교를 원한다면 어떨까요? 어쩌면 나는 각각 x
이 각각 보다 큰지 알고 싶습니다 y
...
// gt :: Number -> Number -> Bool
const gt = x => y =>
x > y
// arrayGt :: [a] -> [a] -> Bool
const arrayGt = arrayCompare (gt)
const xs = [5,10,20]
const ys = [2,4,8]
console.log (arrayGt (xs) (ys)) //=> true
// (5 > 2) && (10 > 4) && (20 > 8) //=> true
const zs = [6,12,24]
console.log (arrayGt (xs) (zs)) //=> false
// (5 > 6) //=> false
더 적은
더 적은 코드로 더 많은 일을하고 있음을 알 수 있습니다. arrayCompare
자체적으로 복잡한 것은 없으며 우리가 만든 각 커스텀 비교기는 매우 간단한 구현을 가지고 있습니다.
쉽게, 우리는 두 배열을 비교하는 우리가 원하는 방법을 정확하게 정의 할 수 있습니다 - 얕은 깊이, 엄격한, 느슨한, 일부 객체 속성, 또는 임의의 계산, 또는 이들의 조합 - 모두가 하나 개의 절차를 사용하여 , arrayCompare
. 어쩌면 RegExp
비교기를 꿈꾸기도합니다 ! 아이들이 어떻게 정규 표현식을 좋아하는지 알고 있습니다.…
가장 빠릅니까? 아니. 그러나 아마도 둘 중 하나 일 필요는 없습니다. 속도가 코드의 품질을 측정하는 데 사용되는 유일한 지표 인 경우, 정말 많은 훌륭한 코드가 버려 질 것입니다. 이것이 바로이 방법을 Practical Way 라고 부르는 이유 입니다. 아니면 더 공정, 할 수 실용적인 방법. 이 답변은 다른 답변과 비교하여 실용적이라고 말하지 않기 때문에이 답변에 적합합니다. 객관적으로 사실입니다. 우리는 추론하기 쉬운 코드가 거의 없어 높은 수준의 실용성을 달성했습니다. 다른 코드에서는이 설명을 얻지 못했다고 말할 수 없습니다.
그것이 당신에게 "올바른"솔루션이됩니까? 그것은 당신 이 결정할 일입니다. 그리고 아무도 당신을 위해 그렇게 할 수 없습니다. 오직 당신 만이 당신의 요구를 알고 있습니다. 거의 모든 경우에, 나는 영리하고 빠른 종류보다 간단하고 실용적이며 다양한 코드를 소중하게 생각합니다. 당신이 중요하게 생각하는 것은 다를 수 있으므로 자신에게 맞는 것을 선택하십시오.
편집하다
나의 오래된 대답은 arrayEqual
작은 절차로 분해하는 데 더 집중되었습니다 . 그것은 흥미로운 운동이지만 실제로이 문제에 접근하는 가장 (가장 실용적인) 방법은 아닙니다. 관심이 있으시면이 개정 내역을 볼 수 있습니다.