2012-08-15 6 views
7

У меня есть метод C#, который выглядит немного как это:Как я могу реорганизовать требуемое условие else?

bool Eval() { 
    // do some work 
    if (conditionA) { 
    // do some work 
    if (conditionB) { 
     // do some work 
     if (conditionC) { 
     // do some work 
     return true; 
     } 
    } 
    } 
    return false; 
} 

В F # это заканчивает тем, что совсем немного уродливее из обязательных либо еще филиалов:

let eval() = 
    // do some work 
    if conditionA then 
    // do some work 
    if conditionB then 
     // do some work 
     if conditionC then 
     // do some work 
     true 
     else 
     false 
    else 
     false 
    else 
    false 

Что бы чище способ записать это в F #?

+2

есть определенно более чистый способ написать это в C#. Обрати свои условия и верните false. Не уверен, что это помогает с F #. –

+0

Wow - много хороших ответов на этот вопрос. –

+0

@ Онорио - я не согласен; есть много «интересных» ответов, но, на мой взгляд, большинство ответов добавляют сложность, и я думал, что наша цель в качестве разработчиков программного обеспечения заключается в ее сокращении. – Brian

ответ

6

Ну, так как «делать какую-то работу» уже императив (предположительно), то я думаю, что

let eval() = 
    let mutable result = false 
    ... // ifs 
     result <- true 
    ... // no more elses 
    result 

короче и разумно. (Другими словами, else является обязательным только для if выражений, которые возвращают значения, так как вы делаете императивный работу, использовать if заявления, которые не нуждаются в else.)

+0

Выбрано по своей простоте и применимо к другим языкам. – Asik

4

Использование функций высшего порядка в Option модуле может сделать этот поток очень чисто без изменяемого состояния:

let Eval() = 
    // do some work 
    if not conditionA then None else 
     // do some work 
     Some state 
    |> Option.bind (fun state -> 
     if not conditionB then None else 
      // do some work 
      Some state') 
    |> Option.bind (fun state -> 
     if not conditionC then None else 
      // do some work 
      Some true) 
    |> defaultArg <| false 

Или для большей ясности, используя названные функции, а не лямбды:

let Eval() = 
    let a() = 
     if not conditionA then None else 
      // do some work 
      Some state 
    let b state = 
     if not conditionB then None else 
      // do some work 
      Some state' 
    let c state = 
     if not conditionC then None else 
      // do some work 
      Some true 
    // do some work 
    a() |> Option.bind b |> Option.bind c |> defaultArg <| false 
+0

Скорее Haskellish (это означает, что мне это нравится) - это использует тот факт, что Option является монадой. Это позор F # не приходит с выражением вычисления для монады Option действительно.В Интернете есть несколько реализаций, но было бы здорово, если бы это было в ядре. –

6

Пожалуйста, не бойтесь извлекать функции. Это ключ к управлению сложной логикой.

let rec partA() = 
    // do some work 
    let aValue = makeA() 
    if conditionA 
    then partB aValue 
    else false 
and partB aValue = 
    // do some work 
    let bValue = makeB aValue 
    if conditionB 
    then partC bValue 
    else false 
and partC bValue = 
    // do some work 
    conditionC 
+0

Это предполагает, что части не делят состояние. – Daniel

+2

@ Daniel - Состояние может передаваться как параметры. – ChaosPandion

13
module Condition = 
    type ConditionBuilder() = 
    member x.Bind(v, f) = if v then f() else false 
    member x.Return(v) = v 
    let condition = ConditionBuilder() 

open Condition 

let eval() = 
    condition { 
    // do some work 
    do! conditionA 
    // do some work 
    do! conditionB 
    // do some work 
    do! conditionC 
    return true 
    } 
+3

@Brian Я считаю, что ваш комментарий немного не в строю. Люди могут сами решить, что есть и что не читаемо, а что есть и что не является хорошим ответом. Этика в стороне, читаемость в глазах смотрящего - разные люди из разных слоев мира легче или труднее читать разные стили кодирования. –

+2

Мое мнение таково, что это ужасный ответ. Я думаю, что подавляющее большинство программистов предпочли бы видеть исходный код или код, похожий на мой, с базовыми if-thens и одной локальной изменяемой переменной, чем этот код, который использует функции эзотерического языка, чтобы ввести новую абстракцию управления, чтобы ... Я не знаю, что, быть более элегантным? Сохранить одну строку? Я просто не понимаю. Я думаю, в сообществе FP есть несколько субкультур, и я совершенно не связан с этим. – Brian

+5

@Brian: FWIW, я тоже не являюсь частью субкультуры FP. Но вы можете утверждать, что это реклама для F #, поскольку она должна быть: 1) интересной/забавной, 2) демонстрировать довольно разнообразный инструментарий F # для решения проблем и 3) привлекать внимание к концепциям, которые заслуживают изучения и могут быть новыми для некоторые программисты (монады). Это что-то? – Daniel

9

Как уже упоминалось в комментариях, вы можете инвертировать состояние. Это упрощает код C#, потому что вы можете написать:

if (!conditionA) return false; 
// do some work 

Хотя F # не имеет императивные возвращается (если вы хотите, чтобы вернуться, вам нужно как истинные и ложные ветви), он на самом деле упрощает этот код немного слишком, потому что вы можете написать:

let eval() = 
    // do some work 
    if not conditionA then false else 
    // do some work 
    if not conditionB then false else 
    // do some work 
    if not conditionC then false else 
    // do some work 
    true 

вы все еще должны написать false несколько раз, но, по крайней мере, вы не должны отступы ваш код слишком далеко. Существует неограниченное количество сложных решений, но это, вероятно, самый простой вариант. Что касается более сложного решения, вы можете использовать F# computation expression that allows using imperative-style returns. Это похоже на вычисление Даниила, но немного более мощное.

1

Вы можете сделать свой код в виде таблицы истинности, которые в зависимости от вашего реального мира случае может сделать его более явным:

let condA() = true 
let condB() = false 
let condC() = true 

let doThingA() = Console.WriteLine("Did work A") 
let doThingB() = Console.WriteLine("Did work B") 
let doThingC() = Console.WriteLine("Did work C") 

let Eval() : bool = 
    match condA(), condB(), condC() with 
    | true, false, _  -> doThingA();       false; 
    | true, true, false -> doThingA(); doThingB();    false; 
    | true, true, true -> doThingA(); doThingB(); doThingC(); true; 
    | false, _,  _  ->          false; 
+0

... хотя менее эффективны, так как condB() и condC() могут быть вызваны без необходимости. – Kit

+0

Чтобы увидеть силу этого подхода BTW, попробуйте прокомментировать, скажем, второе условие соответствия. В VS вы получаете синюю вигглю и предупреждение о том, что вы не охватили все случаи. – Kit

+1

Мне нравится этот, сопоставление образцов может быть предельно ясным. У этого также есть отличное чувство erlang к нему :) – 7sharp9

Смежные вопросы