Дерево решений (рекурсивный тип)

Я застрял на одном из примеров из главы 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";}
...

Это очень интересный вопрос. Я не могу найти документацию о том, как and должен вести себя с let. Кажется, что ваш код либо должен выдавать ошибку, либо то, что вы пытались сделать, должно работать, и вы обнаружили ошибку.

Robert Sim 01.05.2018 23:50

Кстати, я не думаю, что ключевое слово rec необходимо в вашем определении tree.

Robert Sim 01.05.2018 23:51
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
2
202
2

Ответы 2

Если я правильно понимаю, похоже, вам здесь не нужно рекурсивное значение. У вас должна быть возможность сразу определить все дерево:

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 в исходном примере.

Robert Sim 01.05.2018 23:57

Действительно, оба обходных пути допустимы, но исходный код (который правильно компилируется в F# 4.1!) Имеет определенную красоту ;-) Можно даже использовать поддерево просто путем прямой ссылки на промежуточный узел, вместо того, чтобы искать его по Заголовок.

Leo Nistor 02.05.2018 09:16

Это очень похоже на ошибку компилятора.

Вот более короткая реплика:

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.

Leo Nistor 02.05.2018 09:06

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