Подтвердить что ты не робот

Какая функциональная замена для операторов if-then?

Я изучаю F # и функциональное программирование и стараюсь делать что-то функциональным способом. Однако, когда дело доходит до перезаписи кода, который я уже написал на С#, я застрял в простых операциях if-then (те, которые только что-то делают, а не возвращают значение). Я знаю, что вы можете сделать это в F #:

if expr then do ()

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

Итак, я что-то упускаю, а если... тогда прекрасно в функциональном мире? Если нет, то каков функциональный эквивалент такого утверждения? Как я мог взять if-then и включить его?

Изменить: я, возможно, задал неправильный вопрос (извините, все еще довольно новый для функционального программирования): Позвольте взять пример реального мира, который заставил меня даже спросить об этом:

if not <| System.String.IsNullOrWhiteSpace(data) then do
    let byteData = System.Text.Encoding.Unicode.GetBytes(data)
    req.ContentLength <- int64 byteData.Length
    let postStream : System.IO.Stream = req.GetRequestStream()
    postStream.Write(byteData, 0, byteData.Length)
    postStream.Flush()
    postStream.Dispose()

Тело этого if-then ничего не возвращает, но я не знаю, как я мог бы сделать это более функциональным (если это возможно). Я не знаю правильной техники для минимизации императивного кода. Учитывая природу F #, довольно легко просто переносить мой С# напрямую, но у меня возникают трудности с его функциональностью. Каждый раз, когда я получаю такой оператор if на С#, и я пытаюсь перенести его на F #, я смущаюсь, что не могу придумать, как сделать код более функциональным.

4b9b3361

Ответ 1

Важным моментом, который до сих пор не упоминался, является разница между if .. then .. else и if .. then без ветвления else.

Если в функциональных языках

Функциональная интерпретация if заключается в том, что она является выражением, которое оценивает некоторое значение. Чтобы оценить значение if c then e1 else e2, вы оцениваете условие c, а затем оцениваете либо e1, либо e2, в зависимости от условия. Это дает результат if .. then .. else.

Если у вас есть только if c then e, то вы не знаете, каков будет результат оценки, если c is false, потому что нет ветки else! Следующее ясно не имеет смысла:

let num = if input > 0 then 10

В F # выражения с побочными эффектами типа printf "hi" возвращают специальное значение типа unit. Тип имеет только одно значение (записанное как ()), и поэтому вы можете написать if, который делает эффект только в одном случае:

let u = if input > 0 then printf "hi" else ()

Это всегда оценивается как unit, но в ветки true он также выполняет побочный эффект. В ветки false он возвращает значение unit. В F # вам не нужно писать бит else () вручную, но концептуально он все еще существует. Вы можете написать:

let u = if input > 0 then printfn "hi"

Что касается вашего дополнительного примера

Код выглядит отлично для меня. Когда вам нужно иметь дело с API, который является обязательным (как и множество библиотек .NET), лучшим вариантом является использование императивных функций, таких как if с unit -прямой веткой.

Вы можете использовать различные настройки, например, представлять свои данные, используя option<string> (вместо просто string с null или пустую строку). Таким образом, вы можете использовать None для представления отсутствующих данных, и все остальное будет действительным. Затем вы можете использовать некоторые функции более высокого порядка для работы с параметрами, например Option.iter, который вызывает заданную функцию, если есть значение:

maybeData |> Option.iter (fun data ->
    let byteData = System.Text.Encoding.Unicode.GetBytes(data)  
    req.ContentLength <- int64 byteData.Length  
    use postStream = req.GetRequestStream()  
    postStream.Write(byteData, 0, byteData.Length) )

Это не менее важно, но более декларативно, потому что вам не нужно писать if самостоятельно. BTW: Я также рекомендую использовать use, если вы хотите Dispose объект автоматически.

Ответ 2

Нет ничего плохого в if-then в функциональном мире.

Ваш пример действительно похож на let _ = expr, поскольку expr имеет побочные эффекты, и мы игнорируем его возвращаемое значение. Более интересный пример:

if cond then expr

что эквивалентно:

match cond with
| true -> expr
| false -> ()

если мы используем сопоставление шаблонов.

Когда условие простое или существует только одно условное выражение, if-then более читаемо, чем сопоставление шаблонов. Более того, стоит отметить, что все в функциональном программировании является выражением. Поэтому if cond then expr на самом деле является ярлыком if cond then expr else ().

Если-то сама по себе не является императивной, используя if-then, поскольку утверждение является императивным способом мышления. По моему опыту, функциональное программирование - это скорее образ мышления, чем конкретные потоки управления в языках программирования.

EDIT:

Ваш код полностью читаем. Некоторые второстепенные моменты избавляются от избыточного ключевого слова do, аннотации типа и postStream.Dispose() (с помощью ключевого слова use):

if not <| System.String.IsNullOrWhiteSpace(data) then
    let byteData = System.Text.Encoding.Unicode.GetBytes(data)
    req.ContentLength <- int64 byteData.Length
    use postStream = req.GetRequestStream()
    postStream.Write(byteData, 0, byteData.Length)
    postStream.Flush()

Ответ 3

Чтобы написать сложный код, вам нужно разветкиться в какой-то момент. Там очень ограниченное количество способов сделать это, и все они требуют логического потока через раздел кода. Если вы хотите избежать использования if/then/else, вы можете обмануть с помощью цикла /while/repeat, но это сделает ваш код намного менее разумным для обслуживания и чтения.

Функциональное программирование не означает, что вы не должны выполнять инструкции один за другим - это просто означает, что вы не должны иметь изменяемое состояние. Каждая функция должна надежно вести себя одинаково каждый раз, когда она вызывается. Любые различия в том, как данные обрабатываются им, должны учитываться данными, которые передаются, вместо какого-либо триггера, который скрыт от того, что вызывает функцию.

Например, если у нас есть функция foo(int, bool), которая возвращает что-то другое в зависимости от того, является ли bool true или false, почти наверняка будет оператор if где-то в foo(). Это совершенно законно. То, что НЕ является законным, состоит в том, чтобы иметь функцию foo(int), которая возвращает что-то другое в зависимости от того, будет ли она впервые вызвана в программе. Это функция "с точки зрения состояния", и это затрудняет жизнь любому, кто поддерживает программу.

Ответ 4

Это не неотрицательное if-выражение, это то, что происходит в if-выражении. Например, let abs num = if num < 0 then -num else num - полностью функциональный способ записи функции abs. Он принимает аргумент и возвращает преобразование этого аргумента без побочных эффектов. Но когда у вас есть "код, который только что-то делает, а не возвращает значение", тогда вы пишете что-то, что не является чисто функциональным. Цель функционального программирования - минимизировать часть вашей программы, которая может быть описана именно так. Как вы пишете свои условности, тангенциально.

Ответ 5

Есть два наблюдения, которые могут помочь в переходе от императивного к функциональному ( "все является выражением" ) программирования:

  • unit - это значение, тогда как выражение, возвращающее void в С#, рассматривается как оператор. То есть, С# делает различие между операторами и выражениями. В F # все это выражение.

  • В значениях С# можно игнорировать; в F # они не могут, поэтому обеспечивают более высокий уровень безопасности типов. Эта ясность делает программы F # более понятными и предоставляет большие гарантии.

Ответ 6

Он считается функциональным, если ваш оператор if имеет возвращаемое значение и не имеет побочных эффектов.

Предположим, вы хотели написать эквивалент:

if(x > 3) n = 3; else n = x;

Вместо этого вы используете оператор return из команды if:

let n = (if x > 3 then 3 else x)

Эта гипотетическая if внезапно функционирует, потому что она не имеет побочных эффектов; он возвращает только значение. Подумайте об этом, как если бы это был тернарный оператор на некоторых языках: int n = x>3?3:x;

Ответ 7

Мои извинения за незнание F #, но вот одно из возможных решений в javascript:

function $if(param) {
    return new Condition(param)
}

function Condition(IF, THEN, ELSE) {
    this.call = function(seq) {
        if(this.lastCond != undefined) 
            return this.lastCond.call(
                sequence(
                    this.if, 
                    this.then, 
                    this.else, 
                    (this.elsif ? this.elsif.if : undefined),
                    seq || undefined
                )
            );
         else 
            return sequence(
                this.if, 
                this.then, 
                this.else, 
                (this.elsif ? this.elsif.if : undefined),
                seq || undefined
            )
    }


    this.if   = IF ? IF : f => { this.if = f; return this };
    this.then = THEN ? THEN : f => { this.then = f; return this };
    this.else = ELSE ? ELSE : f => { this.else = f; return this };
    this.elsif = f => {this.elsif = $if(f); this.elsif.lastCond = this; return this.elsif};
}

function sequence(IF, THEN, ELSE, ELSIF, FINALLY) {
    return function(val) {
        if( IF(val) ) 
            return THEN();

        else if( ELSIF && ELSIF(val) ) 
            return FINALLY(val);

        else if( ELSE ) 
            return ELSE();

        else 
            return undefined

    }
}}

Функция $if возвращает объект с конструкцией if..then..else..elsif, используя конструктор Condition. После вызова Condition.elsif() вы создадите другой объект Condition - по существу создаете связанный список, который может быть рекурсивно пройден с использованием последовательности()

Вы можете использовать его как:

var eq = val => x => x == val ? true : false;

$if( eq(128) ).then( doStuff ).else( doStuff )
.elsif( eq(255) ).then( doStuff ).else( doStuff ).call();

Однако я понимаю, что использование объекта не является чисто функциональным подходом. Итак, в этом случае вы можете отказаться от объекта все вместе:

sequence(f, f, f, f,
    sequence(f, f, f, f
        sequence(f, f, f)
    )
);

Вы можете видеть, что магия действительно находится в функции sequence(). Я не буду пытаться ответить на ваш конкретный вопрос в javascript. Но я считаю, что основной задачей является то, что вы должны создать функцию, которая будет запускать произвольные функции в операторе if..then, а затем вложить кратность этой функции с помощью рекурсии для создания сложной логической цепочки. По крайней мере, таким образом вам не придется повторять себя;)

Этот код является всего лишь прототипом, поэтому, пожалуйста, возьмите его как доказательство концепции и дайте мне знать, если найдете какие-либо ошибки.