Как реализовать словарь в качестве функции в OCaml? - программирование
Подтвердить что ты не робот

Как реализовать словарь в качестве функции в OCaml?

Я изучаю Джейсон Хикки Введение в Objective Caml.


Вот упражнение, в котором у меня нет подсказки

enter image description hereenter image description here


Прежде всего, что означает реализовать dictionary как function? Как я могу это сделать?

Нужен ли нам какой-нибудь array или что-то в этом роде? По-видимому, мы не можем иметь массив в этом упражнении, потому что array еще не введен в Chapter 3. Но How do I do it without some storage?

Поэтому я не знаю, как это сделать, мне нужны подсказки и руководства.

4b9b3361

Ответ 1

Я думаю, что смысл этого упражнения - заставить вас использовать закрытие. Например, рассмотрим следующую пару функций OCaml в файле fun-dict.ml:

let empty (_ : string) : int = 0
let add d k v = fun k' -> if k = k' then v else d k'

Затем в приглашении OCaml вы можете сделать:


# #use "fun-dict.ml";;
val empty : string -> int = 
val add : ('a -> 'b) -> 'a -> 'b -> 'a -> 'b = 
# let d = add empty "foo" 10;;
val d : string -> int = 
# d "bar";;  (* Since our dictionary is a function we simply call with a
                  string to look up a value *)
- : int = 0   (* We never added "bar" so we get 0 *)
# d "foo";;
- : int = 10  (* We added "foo" -> 10 *)

В этом примере словарь представляет собой функцию с ключом string для значения int. Функция empty - это словарь, который отображает все ключи в 0. Функция add создает замыкание, которое принимает один аргумент, ключ. Помните, что наше определение словаря здесь является функцией от ключа к значениям, поэтому это закрытие является словарем. Он проверяет, есть ли k' (параметр закрытия) = k, где k - только что добавленный ключ. Если он возвращает новое значение, в противном случае он вызывает старый словарь.

Фактически у вас есть список замыканий, которые привязаны не к cons-клеткам, путем закрытия над следующим словарем (функцией) в цепочке).

Дополнительное упражнение, как бы вы удалили ключ из этого словаря?


Изменить: что такое закрытие?

Закрытие - это функция, которая ссылается на переменные (имена) из области, в которой она была создана. Так что это значит?

Рассмотрим нашу функцию add. Он возвращает функцию

fun k' -> if k = k' then v else d k

Если вы смотрите только на эту функцию, есть три имени, которые не определены, d, k и v. Чтобы выяснить, что они собой представляют, мы должны смотреть в охватывающей области, то есть области add. Где мы находим

let add d k v = ...

Итак, даже после того, как add вернула новую функцию, функция все еще ссылается на аргументы для добавления. Таким образом, замыкание является функцией, которая должна быть закрыта некоторой внешней областью, чтобы иметь смысл.

Ответ 2

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

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

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

Чтобы начать думать о более высоких функциях, вот пример. Функция bump принимает функцию (f: int -> int) и int (k: int). Он возвращает новую функцию, которая возвращает значение, которое k больше, чем f возвращает для одного и того же ввода.

let bump f k = fun n -> k + f n

(Дело в том, что bump, например add, принимает функцию и некоторые данные и возвращает новую функцию, основанную на этих значениях.)

Ответ 3

Я подумал, что стоит добавить, что функции в OCaml - это не просто фрагменты кода (в отличие от C, С++, Java и т.д.). В тех нефункциональных языках функции не имеют никакого состояния, связанного с ними, было бы смешно говорить об этом. Но это не относится к функциям в функциональных языках, вы должны начать думать о них как о объектах; странный вид объектов, да.

Итак, как мы можем "сделать" эти объекты? Возьмем пример Джеффри:

let bump f k = 
  fun n ->
    k + f n

Теперь что делает bump? Это может помочь вам подумать о bump как конструкторе , с которым вы, возможно, уже знакомы. Что он создает? он строит объект функции (здесь очень грубо говоря). Итак, что состояние делает этот результирующий объект? он имеет две переменные экземпляра (вроде), которые являются f и k. Эти две переменные экземпляра привязаны к результирующему объекту функции при вызове bump f k. Вы можете видеть, что возвращаемый объект-функция:

fun n ->
  k + f n

Использует эти переменные экземпляра f и k в теле. Как только этот объект-функция возвращается, вы можете только вызывать его, нет другого способа доступа к f или k (так это инкапсуляция).

Очень необычно использовать термин function-object, они называются только функциями, но вы должны иметь в виду, что они могут также "заключать" состояние, Эти функции-объекты (также называемые замыканиями) не сильно отделены от "реальных" объектов в объектно-ориентированных языках программирования, очень интересная дискуссия может быть найдена здесь.

Ответ 4

Я тоже борюсь с этой проблемой. Здесь мое решение и оно работает для случаев, перечисленных в учебнике...

Пустой словарь просто возвращает 0:

let empty (k:string) = 0

Поиск вызовов функции словаря на клавише. Эта функция тривиальна:

let find (d: string -> int) k = d k

Добавить расширяет функцию словаря, чтобы иметь другое условное ветвление. Мы возвращаем новый словарь, который берет ключ k 'и сопоставляет его с k (ключ, который нам нужно добавить). Если он совпадает, мы возвращаем v (соответствующее значение). Если он не соответствует, мы возвращаем старый (меньший) словарь:

let add (d: string -> int) k v =
    fun k' ->
        if k' = k then
                v
            else
                d k'

Вы можете изменить add, чтобы иметь функцию удаления. Кроме того, я добавил условие, чтобы убедиться, что мы не удаляем не существующий ключ. Это просто для практики. Эта реализация словаря в любом случае плохая:

let remove (d: string -> int) k =
    if find d k = 0 then
        d
    else
        fun k' ->
            if k' = k then
                    0
                else
                    d k'

Я плохо разбираюсь в терминологии, поскольку я все еще изучаю функциональное программирование. Поэтому, не стесняйтесь меня исправлять.