Я застрял на одном из примеров из главы 8 «Функционального программирования в реальном мире» - деревья решений. Если я запустил этот код в FSI (версия 4.1)
type QueryInfo = {
Title: string
Positive: Decision
Negative: Decision
}
and Decision =
| Result of string
| Query of QueryInfo
let rec tree =
Query({ Title = "A"
Positive = aleft; Negative = aright })
and aleft =
Query({ Title = "B"
Positive = bleft; Negative = Result("Yes") })
and aright =
Query({ Title = "C"
Positive = Result("No"); Negative = Result("Yes") })
and bleft =
Query({ Title = "D"
Positive = Result("No"); Negative = Result("Yes") })
printfn "%A" tree
значение tree
равно
val tree : Decision = Query {Title = "A";
Positive = null;
Negative = null;}
val aleft : Decision = Query {Title = "B";
Positive = null;
Negative = Result "Yes";}
val aright : Decision = Query {Title = "C";
Positive = Result "No";
Negative = Result "Yes";}
val bleft : Decision = Query {Title = "D";
Positive = Result "No";
Negative = Result "Yes";}
значения для Positive
и Negative
для первых двух узлов - null
, вместо ссылок, объявленных ниже (aleft
, aright
, bleft
). Следовательно, функция для анализа дерева потерпит неудачу.
Как мне определить (рекурсивный) тип дерева решений, чтобы получить значение древовидной структуры?
val tree : Decision = Query {Title = "A";
Positive = aleft;
Negative = aright;}
val aleft : Decision = Query {Title = "B";
Positive = bleft;
Negative = Result "Yes";}
...
Кстати, я не думаю, что ключевое слово rec
необходимо в вашем определении tree
.
Если я правильно понимаю, похоже, вам здесь не нужно рекурсивное значение. У вас должна быть возможность сразу определить все дерево:
let tree =
Query { Title = "A"
Positive = Query { Title = "B"
Positive = Query { Title = "D"
Positive = Result("No")
Negative = Result("Yes") }
Negative = Result "Yes" }
Negative = Query { Title = "C"
Positive = Result "No"
Negative = Result "Yes" } }
Печать tree
дает следующий результат:
Query {Title = "A";
Positive = Query {Title = "B";
Positive = Query {Title = "D";
Positive = Result "No";
Negative = Result "Yes";};
Negative = Result "Yes";};
Negative = Query {Title = "C";
Positive = Result "No";
Negative = Result "Yes";};}
Другой вариант - объявить tree
, начиная с листьев до корня, что устраняет необходимость в and
в исходном примере.
Действительно, оба обходных пути допустимы, но исходный код (который правильно компилируется в F# 4.1!) Имеет определенную красоту ;-) Можно даже использовать поддерево просто путем прямой ссылки на промежуточный узел, вместо того, чтобы искать его по Заголовок.
Это очень похоже на ошибку компилятора.
Вот более короткая реплика:
type A = A of B
and B = B of A | C
let rec a = B (A b)
and b = B (A c)
and c = C
Инициализация здесь не выполняется точно так же: a = B (A null)
.
Подал здесь, посмотрим, какой ответ я получу.
Возможно, это ошибка компилятора. Использование F# версии 4.0, как здесь, https://repl.it/@leonistor/RecDecisionTree дает желаемый результат, как показано выше @ taylor-wood.
Это очень интересный вопрос. Я не могу найти документацию о том, как
and
должен вести себя сlet
. Кажется, что ваш код либо должен выдавать ошибку, либо то, что вы пытались сделать, должно работать, и вы обнаружили ошибку.