Вопрос

Alright, I'm probably doing this wrong but it has got me pulling my hair out. I haven't been able to find anything to do what I want

Take this pseudocode

my_function left right
    = another_function new_left new_right (fourth_function new_left new_right)
        where new_left = if some_condition then left else third_function left
            new_right = if some_condition then third_function right else right

How can I avoid rechecking some_condition? And I'm not talking about saving some_condition as another variable in the where construct. If I put lets inside the if I then duplicate the in another_function new_left new_right.

In an imperative language I could do something like

int new_left;
int new_right;
if (condition) {
    new_left = left;
    new_right = third_function(right);
} else {
    new_left = third_function(left);
    new_right = right;
}
return another_function(new_left, new_right, fourth_function(new_left, new_right));

I know in a functional language you're not supposed to think of doing things in a sequence, but rather as a composition of expressions, so I'm just looking for a way to write the original pseudocode such that it's DRY. And it seems like a simple and relatively common case.

Edit

Sorry for the confusion. I can't inline third_function left/right because I need to use it's value twice (updated pseudocode). And fourth_function can't be moved inside another_function

Это было полезно?

Решение

How about

my_function left right | somecondition = 
                         another_function left (third_function right)
                       | otherwise     =
                         another_function (third_function left) right

With the new edit

my_function left right = ...
  where (new_left, new_right) | somecondition = (left, third_function right)
                              | otherwise     = (third_function left, right)

Другие советы

Perhaps the quickest way would be

my_function left right
      = uncurry another_function $ if some_condition
                                    then (left, third_function right)
                                    else (third_function left, right)

But jozefg's suggestion is rather cleaner IMO.


As for the updated question: when re-using results it's generally good to actually make them named variables, again jozefg showed how to do that in your problem. But (also bridging to your imperative thing): Haskell has monads, which can be used for anything imperative (and of course much more). In your case, the Reader monad, aka function does the trick:

import Control.Monad.Instances

my_function left right = 
        ( uncurry fourth_function >>= flip (uncurry another_function) )
         $ if some_condition
            then (left, third_function right)
            else (third_function left, right)

but this style's readability is rather questionable.

It does get a little better with the helpers for arrows, of which functions again are a special case:

import Control.Arrow

my_function = curry $
        ( uncurry fourth_function >>= flip (uncurry another_function) )            
        . (if some_condition then second else first) third_function

If just use simple function programming, yes, we could use "if".

If we have few "left"s, Monads or Monoids (Maybe a, Either a b, Any a, All a, ...) are suitable.:

foo = do
   if cond1 then Just smths1 else Nothing
   if cond2 then Just smths2 else Nothing
   if cond2 then Just smths2 else Nothing
   if cond2 then Just smths2 else Nothing
   if cond2 then Just smths2 else Nothing

If we have a lot of "left" and "right", arrows are suitable:

foo = proc (l,r) -> do
    nl <- (if cond1 then idA else arr . foo1)   -< l
    nr <- (if cond2 then arr . foo2 else idA )  -< r
    ...
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top