我需要根据条件为变量分配一个值。我想在考虑功能性编程范例的情况下进行此操作,因此无法在外部范围中对其进行声明然后重新分配。
// let foo = undefined // no-let!
if(condition) {
const foo = 1
} else {
const foo = 0
}
do_some_stuff(foo) // Uncaught ReferenceError: foo is not defined
我知道,我可以在这里使用三元表达式,例如
const foo = condition ? 1 : 0
但是,如果我在自己的条件内还有其他例程可以做,例如:
if(condition) {
const foo = 1
do_stuff()
} else {
const foo = 0
do_other_stuff()
}
do_third_stuff(foo)
答案 0 :(得分:2)
没有什么可以阻止您分裂两者:
const foo = condition ? 1 : 0;
if(condition) {
do_stuff();
} else {
do_other_stuff();
}
do_third_stuff(foo);
如果condition
是昂贵的执行,只需在多次使用之前将其分配给变量:
let isFoo = expensiveIsFooMethod();
const foo = isFoo ? 1 : 0;
if(isFoo) {
do_stuff();
} else {
do_other_stuff();
}
do_third_stuff(foo);
您是对的,如果您不必重复该条件,它会更干净,但是您已经引入了此限制,因为您使用的是const
变量,因此无法赋值到您的const
的多个位置。
我建议在这里胜过这两个选择。对您来说更重要的是:更简洁的语法,还是确保您永远不会覆盖该值?
答案 1 :(得分:1)
可能您会使用Either
之类的代数数据类型(ADT)对案件进行编码。也就是说,您可能会涉及两个子情况: left 和 right 。
从// --> Solution starts here
开始查看代码。先前的代码是使用香草JavaScript来使代码可运行的微型标准FP库。检查一下,享受吧!
// Mini standard library
// -------------------------------
// The identity combinator
// I :: a -> a
const I = x => x
// Either ADT
const Either = (() => {
// Creates an instance of Either.Right
//
// of :: b -> Either a b
const of = x => ({ right: x })
// Creates an instance of Either.Right
//
// Right :: b -> Either a b
const Right = of
// Creates an instance of Either.Left
//
// Left :: a -> Either a b
const Left = x => ({ left: x })
// Maps Either.Left or Either.Right in a single operation
//
// bimap :: (a -> c) -> (b -> d) -> Either a b -> Either c -> d
const bimap = f => g => ({ left, right }) => left ? Left (f (left)) : Right (g (right))
// Lifts a value to Either based on a condition, where false
// results in Left, and true is Right.
//
// tagBy :: (a -> Boolean) -> a -> Either a a
const tagBy = f => x => f (x) ? Right (x) : Left (x)
// Unwraps Either.Left or Either.Right with mapping functions
//
// either :: (a -> c) -> (b -> c) -> Either a b -> c
const either = f => g => ({ left, right }) => left ? f (left) : g (right)
// Unwraps Either.Left or Either.Right and outputs the raw value on them
//
// unwrap :: Either a b -> c
const unwrap = either (I) (I)
return { of, Right, Left, bimap, tagBy, either, unwrap }
}) ()
// --> Solution starts here
// Lifts to Either.Right if x is greater than 3,
// otherwise, x is encoded as Left.
//
// tagGt3 :: Number -> Either Number Number
const tagGt3 = Either.tagBy (x => x > 3)
// doStuff :: Number -> Number
const doStuff = x => x + 1
// doStuff2 :: Number -> Number
const doStuff2 = x => x * 4
// doStuff3 :: Either Number Number -> Either Number Number
const doStuff3 = Either.bimap (doStuff) (doStuff2) // <-- here's the decision!
const eitherValue1 = doStuff3 (tagGt3 (2))
const eitherValue2 = doStuff3 (tagGt3 (30))
const output1 = Either.unwrap (eitherValue1)
const output2 = Either.unwrap (eitherValue2)
console.log ('output1: ', output1)
console.log ('output2: ', output2)
现在,我介绍pipe
来粘合一个或多个一元函数的组合,这使代码更加优雅:
// Mini standard library
// -------------------------------
// The identity combinator
// I :: a -> a
const I = x => x
// Pipes many unary functions
//
// pipe :: [a -> b] -> a -> c
const pipe = xs => x => xs.reduce ((o, f) => f (o), x)
// Either ADT
const Either = (() => {
// Creates an instance of Either.Right
//
// of :: b -> Either a b
const of = x => ({ right: x })
// Creates an instance of Either.Right
//
// Right :: b -> Either a b
const Right = of
// Creates an instance of Either.Left
//
// Left :: a -> Either a b
const Left = x => ({ left: x })
// Maps Either.Left or Either.Right in a single operation
//
// bimap :: (a -> c) -> (b -> d) -> Either a b -> Either c -> d
const bimap = f => g => ({ left, right }) => left ? Left (f (left)) : Right (g (right))
// Lifts a value to Either based on a condition, where false
// results in Left, and true is Right.
//
// tagBy :: (a -> Boolean) -> a -> Either a a
const tagBy = f => x => f (x) ? Right (x) : Left (x)
// Unwraps Either.Left or Either.Right with mapping functions
//
// either :: (a -> c) -> (b -> c) -> Either a b -> c
const either = f => g => ({ left, right }) => left ? f (left) : g (right)
// Unwraps Either.Left or Either.Right and outputs the raw value on them
//
// unwrap :: Either a b -> c
const unwrap = either (I) (I)
return { of, Right, Left, bimap, tagBy, either, unwrap }
}) ()
// --> Solution starts here
// doStuff :: Number -> Number
const doStuff = x => x + 1
// doStuff2 :: Number -> Number
const doStuff2 = x => x * 4
const { tagBy, bimap, unwrap } = Either
// doStuff3 :: Number -> Number
const doStuff3 = pipe ([
tagBy (x => x > 3),
bimap (doStuff) (doStuff2), // <-- here's the decision!
unwrap
])
const output1 = doStuff3 (2)
const output2 = doStuff3 (30)
console.log ('output1: ', output1)
console.log ('output2: ', output2)
答案 2 :(得分:0)
因为您不想在外部声明 foo ,所以为什么不这样简单:
if(condition) {
const foo = 1
do_stuff()
do_third_stuff(foo)
} else {
const foo = 0
do_other_stuff()
do_third_stuff(foo)
}