기능 (ECMAScript)
함수 정의와 함수 호출 만 있으면됩니다. 분기, 조건부, 연산자 또는 내장 함수가 필요하지 않습니다. ECMAScript를 사용한 구현을 보여 드리겠습니다.
먼저 true
and 라는 두 함수를 정의 해 봅시다 false
. 원하는 방식으로 정의 할 수 있지만 완전히 임의적이지만 나중에 볼 수있는 몇 가지 장점이있는 매우 특별한 방식으로 정의 할 것입니다.
const tru = (thn, _ ) => thn,
fls = (_ , els) => els;
tru
단순히 두 번째 인수를 무시하고 첫 번째를 반환하는 두 개의 매개 변수가있는 함수입니다. fls
또한 첫 번째 인수를 무시하고 두 번째 매개 변수를 반환하는 두 개의 매개 변수가있는 함수입니다.
왜 우리는 인코딩 않았다 tru
및 fls
이 방법은? 음, 이런 식으로, 두 함수는 두 개념을 나타내지 true
하고 false
즉, 아니, 같은 시간에, 그들은 또한 "선택"의 개념을 표현, 그들은 또한 있습니다 if
/ then
/ else
표현! if
조건을 평가하고 then
블록과 else
블록을 인수로 전달합니다. 조건이로 평가 tru
되면 then
블록 을 반환하고로 평가 fls
하면 else
블록 을 반환합니다 . 예를 들면 다음과 같습니다.
tru(23, 42);
// => 23
이 반환 23
하고이 :
fls(23, 42);
// => 42
42
예상 한대로를 반환합니다 .
그러나 주름이 있습니다.
tru(console.log("then branch"), console.log("else branch"));
// then branch
// else branch
이것은 인쇄 모두 then branch
와 else branch
! 왜?
음, 반환 첫 번째 인수의 반환 값을하지만 평가 ECMAScript를가 엄격 항상 함수를 호출하기 전에 함수에 모든 인수를 평가하기 때문에, 두 인수를. IOW : 그것은 첫 번째 인자 평가 console.log("then branch")
단순히 반환 undefined
인쇄 및 부작용 갖는 then branch
콘솔을, 그리고 는 번째 인자를 평가할 수도있는 복귀 undefined
부작용 등 콘솔과 인쇄. 그런 다음 첫 번째를 반환합니다 undefined
.
이 인코딩이 발명 된 λ- 미적분학에서는 문제가되지 않습니다 : λ- 미적분학은 순수합니다 . 즉, 부작용이 없습니다. 따라서 두 번째 인수도 평가됩니다. 또한 λ- 미적분은 게 으르 거나 (또는 적어도 정상적인 순서로 평가되는 경우가 많음) 실제로 필요하지 않은 인수는 평가하지 않습니다. 그래서, IOW : λ- 미적분에서 두 번째 논증은 결코 평가되지 않을 것이며, 만약 그렇다면, 우리는 눈치 채지 못할 것입니다.
그러나 ECMAScript는 엄격합니다 . 즉, 항상 모든 인수를 평가합니다. 실제로 항상 그런 것은 아닙니다. 예를 들어, if
/ then
/ 는 조건이 조건 인 경우 에만 분기를 평가 하고 조건이 조건 인 경우 에만 분기를 평가합니다 . 그리고 우리는이 동작을로 복제하고 싶습니다 . 고맙게도 ECMAScript가 게으르지 않더라도 거의 모든 다른 언어와 마찬가지로 코드 조각의 평가를 지연시키는 방법이 있습니다. 함수로 감싸고 함수를 호출하지 않으면 코드는 절대로 처형되지 않습니다.else
then
true
else
false
iff
따라서 함수에서 두 블록을 모두 래핑하고 마지막에 반환되는 함수를 호출합니다.
tru(() => console.log("then branch"), () => console.log("else branch"))();
// then branch
인쇄 then branch
하고
fls(() => console.log("then branch"), () => console.log("else branch"))();
// else branch
인쇄합니다 else branch
.
우리는 전통적인 구현할 수 if
/ then
/ else
이런 식으로 :
const iff = (cnd, thn, els) => cnd(thn, els);
iff(tru, 23, 42);
// => 23
iff(fls, 23, 42);
// => 42
다시 말하지만, 위와 같은 이유로 iff
의 정의에서 함수와 추가 함수 호출 괄호를 호출 할 때 추가 함수 랩핑이 필요합니다 iff
.
const iff = (cnd, thn, els) => cnd(thn, els)();
iff(tru, () => console.log("then branch"), () => console.log("else branch"));
// then branch
iff(fls, () => console.log("then branch"), () => console.log("else branch"));
// else branch
이제이 두 가지 정의가 있으므로 구현할 수 있습니다 or
. 첫 or
번째 피연산자가 진실이면 표현식의 결과는 첫 번째 피연산자와 동일합니다. 그렇지 않으면 표현식의 결과는 두 번째 피연산자의 결과입니다. 간단히 말해 : 첫 번째 피연산자가 true
이면 첫 번째 피연산자 를 반환하고, 그렇지 않으면 두 번째 피연산자를 반환합니다.
const orr = (a, b) => iff(a, () => a, () => b);
작동하는지 확인하십시오.
orr(tru,tru);
// => tru(thn, _) {}
orr(tru,fls);
// => tru(thn, _) {}
orr(fls,tru);
// => tru(thn, _) {}
orr(fls,fls);
// => fls(_, els) {}
큰! 그러나 그 정의는 약간 추악하게 보입니다. 기억, tru
그리고 fls
이미 그 자체로 조건부 모든처럼 행동 때문에 정말 필요가 없다 iff
, 따라서 모든 기능 포장 모두에서 :
const orr = (a, b) => a(a, b);
거기에는 or
함수 정의와 함수 호출만으로 몇 줄만 정의되어 있습니다 (다른 부울 연산자 포함).
const tru = (thn, _ ) => thn,
fls = (_ , els) => els,
orr = (a , b ) => a(a, b),
nnd = (a , b ) => a(b, a),
ntt = a => a(fls, tru),
xor = (a , b ) => a(ntt(b), b),
iff = (cnd, thn, els) => cnd(thn, els)();
반환 인 ECMAScript에는 기능이나 운영자가없는 : 불행하게도,이 구현은 오히려 쓸모가 없다 tru
거나 fls
, 그들은 모두 반환 true
또는 false
우리는 우리의 기능을 사용할 수 있도록. 하지만 여전히 할 수있는 일이 많습니다. 예를 들어, 이것은 단일 연결 목록의 구현입니다.
const cons = (hd, tl) => which => which(hd, tl),
car = l => l(tru),
cdr = l => l(fls);
객체 (스칼라)
당신은 주목 뭔가 독특한이있을 수 있습니다 : tru
및 fls
이중 역할을, 그들은 데이터 값 역할을 모두 true
하고 false
, 그러나 동시에, 그들은 또한 조건식 역할을합니다. 그들은있는 데이터 및 행동 "일"... 하나 개 ... 음으로 번들 ... 나 (I는 말을 감히) 오브젝트를 !
사실, tru
그리고 fls
객체입니다. 그리고 Smalltalk, Self, Newspeak 또는 기타 객체 지향 언어를 사용한 적이 있다면, 동일한 방식으로 부울을 구현한다는 것을 알게 될 것입니다. 여기 스칼라에서 이러한 구현을 보여줄 것입니다.
sealed abstract trait Buul {
def apply[T, U <: T, V <: T](thn: ⇒ U)(els: ⇒ V): T
def &&&(other: ⇒ Buul): Buul
def |||(other: ⇒ Buul): Buul
def ntt: Buul
}
case object Tru extends Buul {
override def apply[T, U <: T, V <: T](thn: ⇒ U)(els: ⇒ V): U = thn
override def &&&(other: ⇒ Buul) = other
override def |||(other: ⇒ Buul): this.type = this
override def ntt = Fls
}
case object Fls extends Buul {
override def apply[T, U <: T, V <: T](thn: ⇒ U)(els: ⇒ V): V = els
override def &&&(other: ⇒ Buul): this.type = this
override def |||(other: ⇒ Buul) = other
override def ntt = Tru
}
object BuulExtension {
import scala.language.implicitConversions
implicit def boolean2Buul(b: ⇒ Boolean) = if (b) Tru else Fls
}
import BuulExtension._
(2 < 3) { println("2 is less than 3") } { println("2 is greater than 3") }
// 2 is less than 3
이 BTW는 다형성 리팩토링으로 조건부 바꾸기가 항상 작동하는 이유입니다. 방금 보여 드렸듯이, 다형성 메시지 디스패치가 조건부를 간단히 구현하여 조건부를 대체 할 수 있기 때문에 프로그램의 모든 조건부를 다형성 메시지 디스패치로 항상 바꿀 수 있습니다. 스몰 토크, 셀프, 뉴스 피크와 같은 언어는 조건이 없기 때문에 존재 증거입니다. (또한 가상 메소드 호출이라고 불리는 다형성 메시지 디스패치를 제외하고 루프, BTW 또는 실제로 어떤 종류의 언어 내장 제어 구조도 없습니다.)
패턴 매칭 (Haskell)
or
패턴 일치 또는 Haskell의 부분 함수 정의와 같은 것을 사용하여 정의 할 수도 있습니다 .
True ||| _ = True
_ ||| b = b
물론 패턴 일치는 조건부 실행의 한 형태이지만 다시 객체 지향 메시지 디스패치입니다.