OCaml: Как построить форматированную строку питоническим способом?

Все это начинается с простой идеи: как написать строку в формате Python в ocaml.

питонцы могут инициализировать строку как:

str = "this var: %s" % this_var
str2 = "this: %s; that: %s" % (this_var, that_var)

но форматированный строковый код ocaml выглядит следующим образом:

let str = Printf.sprintf "this var: %s" this_var
let str2 = Printf.sprintf "this: %s; that: %s" this_var that_var

Я считал, что могу сделать что-то, чтобы код формирования строки ocaml был похож на Python. Сначала я определил функцию, как показано ниже:

let (%) s x = Printf.sprintf s x

тогда я могу написать прямо как:

let str = "this: %s" % "sth"

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

let (%) s li = 
  let split_list = Str.full_split (regexp "%[a-z]") s in
  let rec fmt result_str s_list x_list = match s_list with
    | [] -> result_str
    | shd::stl -> match shd with
       | Text t ->  fmt (result_str^t) stl x_list
       | Delim d -> match x_list with
          | [] -> fmt result_str stl []
          | xhd::xtl -> fmt (result_str^(Printf.sprintf d xhd)) stl xtl
  in 
  fmt "" split_list li

Но функция просто НЕ МОЖЕТ работать, потому что ошибка типа, а также список ocaml не могут содержать несколько типов. если вы напишете что-то вроде: "name: %s; age: %d" % ["John"; 20] мир компиляторов ocaml посмеется над кодом и скажет вам какую-то ОШИБКУ типа.

Очевидно, я должен использовать Tuple для замены List. но я просто НЕ знаю, как выполнять хвостовую рекурсию кортежа переменной длины.

любое предложение приветствуется. У меня действительно два вопроса.

  1. как написать питонический код ocaml для форматирования строки.
  2. # P10 # # P11 # # P12 #
    # P13 #

person NullPointer    schedule 02.11.2009    source источник


Ответы (5)


На самом деле это выполнимо, если ваш оператор начинается с символа #, поскольку этот символ имеет более высокий приоритет, чем приложение функции.

let (#%) = Printf.sprintf;;
val ( #% ) : ('a, unit, string) format -> 'a = <fun>
"Hello %s! Today's number is %d." #% "Pat" 42;;
- : string = "Hello Pat! Today's number is 42."
person PatJ    schedule 16.04.2018

(1) Я не думаю, что есть хороший способ сделать это лучше, чем использовать Printf.sprintf напрямую. Я имею в виду, что вы можете расширить то, что уже придумали:

let (%) = Printf.sprintf
let str3 = ("this: %s; that: %s; other: %s" % this_var) that_var other_var

который работает, но некрасиво из-за скобок, необходимых для приоритета.

(2) Очень сложно сгенерировать строку формата во время выполнения, потому что строки формата анализируются во время компиляции. Они могут выглядеть как строковые литералы, но на самом деле это другой тип, который является «чем-то форматом6». (он определяет, нужна ли вам строка или строка формата на основе предполагаемого типа) Фактически, точный тип строки формата зависит от того, какие заполнители находятся в формате; это единственный способ проверить количество и типы аргументов формата. Лучше не связываться со строками формата, потому что они очень сильно привязаны к системе типов.

person newacct    schedule 02.11.2009

Зачем нужно заменять статически проверенный sprintf некоторым динамическим форматированием? Программа OCaml Printf компактна в использовании и безопасна во время выполнения. Сравните это с C printf, который является компактным, но небезопасным, и потоками C ++, которые безопасны, но подробны. Формат Python ничем не лучше, чем C printf (за исключением того, что вы получаете исключение вместо аварийного дампа).

Единственный возможный вариант использования - это строка формата, поступающая из внешнего источника. И обычно лучше переместить его во время компиляции. Если это невозможно, то нужно только вернуться к ручному динамическому форматированию с обработкой ошибок (как уже говорилось, вы не можете использовать Printf со строкой динамического формата). Кстати, один из таких случаев - интернационализация - покрывается существующими библиотеками . Как правило, если кто-то хочет динамически комбинировать несколько значений разных типов, их нужно обернуть вариантами (например, ['S "hello"; 'I 20]) и сопоставить с шаблоном на стороне печати.

person ygrek    schedule 03.11.2009

Вам следует проверить расширенный / расширяемый printf OCaml Batteries Included. У меня такое чувство, что ты можешь делать с ним все, что хочешь.

person Yoric    schedule 14.10.2010

Если Ocaml не может динамически сгенерировать строку как format6 str и передать ее sprintf? для кода:

let s = "%s" in Printf.sprintf s "hello"

Что, если мы обойдем систему набора текста ...

external string_to_format :
 string -> ('a, 'b, 'c, 'd, 'e, 'f) format6 = "%identity"

let s = string_to_format "%s" in (Printf.sprintf s "hello" : string);;

Я не утверждаю, что это окончательное решение, но это все, что я понял после просмотра списка рассылки: http://pauillac.inria.fr/caml/FAQ/FAQ_EXPERT-eng.html#printf и ocaml src.

person ruslan    schedule 02.05.2010
comment
Обойти систему типов - не лучшая идея. Это приводит к сбоям программ или их неправильному поведению. - person Gilles 'SO- stop being evil'; 13.11.2011
comment
Что, если мы обойдем систему набора текста ... тогда вам будет лучше использовать C или какой-либо другой язык, который не имеет математически обоснованной защиты от сбоев. Но ваше исследование было тщательным. - person Str.; 21.10.2015