Как условно присвоить переменную функциональным способом? - PullRequest
0 голосов
/ 29 января 2019

Мне нужно присвоить значение переменной на основе условия.Я хочу сделать это с мыслью о функциональной парадигме программирования, поэтому я не могу объявить ее во внешней области и затем переназначить.

// 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)

Ответы [ 3 ]

0 голосов
/ 29 января 2019

Поскольку вы не хотите объявлять foo извне, почему вы не просто так:

if(condition) {
  const foo = 1
  do_stuff()
  do_third_stuff(foo)
} else {
  const foo = 0
  do_other_stuff()
  do_third_stuff(foo)
}
0 голосов
/ 29 января 2019

Возможно, вы закодируете свой случай, используя некоторый алгебраический тип данных (ADT), такой как Either.Таким образом, вы можете охватить два варианта: влево и вправо .

См. Код с // --> Solution starts here и далее.Предыдущий код представляет собой мини-стандартную библиотеку FP, использующую ванильный JavaScript для обеспечения работоспособности кода.Проверьте это и наслаждайтесь!

// 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)
0 голосов
/ 29 января 2019

Ничто не мешает вам разделить два:

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 в нескольких местах.

Я предлагаю перевесить два варианта здесь.Что для вас важнее: более чистый синтаксис или гарантия того, что вы никогда не перезапишите значение?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...