Мы пробуем это в SWI Prolog (если быть точным: SWI-Prolog (threaded, 64 bits, version 8.1.19-37-gc4fb81227)
)
?- format("Hello, ~w\n",[world]).
Hello, world
true.
Работает, но теперь мы добавляем заполнитель в строку (введенный ~
) и забываем добавить соответствующий аргумент:
?- format("Hello, ~w. Goodbye ~w!\n",[world]).
Hello, world. Goodbye
ERROR: Format error: not enough arguments
ERROR: In:
ERROR: [10] format("Hello, ~w. Goodbye ~w!\n",[world])
ERROR: [9] <user>
?-
Или, используя catch/3
:
?- catch(format("Hello, ~w. Goodbye ~w!\n",[world]),
Xc,
format("\n\nOh noes! Got Exception!", [Xc])).
Hello, world. Goodbye
Oh noes! Got Exception!
Xc = error(format('not enough arguments'), context(system:format/2, _6584)).
Теперь, опасаясь сбоя при неправильном количестве аргументов при форматировании вывода в любой типизированный динамический язык c кажется чрезмерным.
Тем более, что format/2
в порядке с аргументом, не являющимся списком, т.е. уже ведет себя снисходительно в случае «одного аргумента, который не является списком»:
?- format("Just one ~w argument", "beautiful").
Just one beautiful argument
true.
То же, что и:
?- format("Just one ~w argument", ["beautiful"]).
Just one beautiful argument
true
Было бы более уместно выдавать «предупреждение» во время выполнения и продолжать, оставляя разработчику исправление программы позже. В производственной системе проблема с format/x
аргументами может не решаться в течение некоторого времени, и, возможно, заставить ее разрушить систему, вероятно, не то, что нужно.
Другими словами, я хотел бы получить format/x
семья ведет себя "снисходительно".
Есть ли способ сделать это, кроме написания моего format/x
вокруг существующего? Как флаг? Другие решения включают чрезмерное защитное кодирование:
- анализирует строку формата в обтекании
my_format/x
вокруг format/x
и либо расширяет список аргументов, либо обнуляет заполнители; - перехватывает исключение в обертке
my_format/x
и подавляет или повторяет попытку вывода со списком аргументов изменений.
Обновление: SWI-Prolog format/2
фактически станет более строгим
Иначе говоря, в обсуждении Слишком много аргументов для форматирования / 2 , было принято решение о создании format/2
для исключения, если слишком много аргументов, так что единственное допустимым вызовом является «точно правильное количество аргументов».
Патч: ИЗМЕНЕНО: формат / 1-3: вызвать исключение, если аргументов больше, чем
Приложение: проверка других систем / API
Ниже приведено длинное и неполное отступление.
Я начал с проверки writef/x
, но затем протестировал несколько других систем ' поведение просто для удовольствия и практики.
Эти "системы" делятся на три основных класса:
- Простые API "format & print" , произошли от C -библиотека
printf
, остроумие Строки формата h, допускающие различные последовательности заполнителей. Предикаты Пролога format/x
(от Квинта Пролога, широко используемые, но нестандартизированные) и writef/x
(от Эдинбурга C Пролог, обезображенные) относятся к этому. Обратите внимание, что символ, представляющий заполнитель, традиционно %
. Это относится к writef/x
, но для format/x
это ~
. - Некоторые (динамические c) языки программирования имеют более простые «строковые интерполяции» , где строковые заполнители заменяются строкой, полученной из значения переменных, доступных в текущем контексте :
"this is a ${adjective} test"
или даже "this is the ${counter.toString(longformat)}th test"
. В Прологе такого не существует.
- API-интерфейсы ведения журналов и библиотеки . Как правило, они легко настраиваются и даже «подключаются» при запуске процесса. В мире Java пакет
java.util.logging
используется по умолчанию с Java 1.4 (2002). Тем не менее, есть другие с другими функциями и поведением, например, " SLF4J + Logback " или Apache Log4j Чеки Гюльчу (который на самом деле предшествует java.util.logging
по немного). - В SWI Prolog есть предикат
print_message/2
. Это следует использовать в предпочтении к безудержным format/x
вызовам, но, тем не менее, это все же, скорее всего, базовый c. В частности, только сообщениям «уровня отладки» можно присвоить «topi c» и отфильтровать на основе этих topi c. Распечатка информационных сообщений контролируется флагом Prolog verbose
(не настраивая уровень логирования). - Существует также библиотека (отладка) , предназначенная для отправки сообщений отладки и вызывающая
print_message/2
. Сообщения назначаются на «topi c». Динамически включая или отключая темы, пользователь может выбирать нужные сообщения. Операторы отладки удаляются, когда код компилируется для оптимизации.
- Библиотеки шаблонов , которые go за пределами попытки генерировать вывод программы с помощью вызовов
format/x
. В мире Java есть, например, Теренс Парр StringTemplate или Apache Velocity . - Что такое эквивалент в Прологе? Пролог, скорее всего, не будет языком для выполнения этой операции, но если бы я абсолютно хотел сгенерировать HTML страниц с использованием Пролога?
И, немного тангенциально, так называется "здесь документы" . Пролог имеет их (более или менее), так как строковый литерал может быть многострочным. Но нет способа выполнить подстановку переменных для такой строки или даже указать правильный отступ, так что она немного слабая.
?- [user].
|: p(X) :- X = "This is some string
followed by several lines
indented".
|: % user://1 compiled 0.00 sec, 1 clauses
true.
?- p(X).
X = "This is some string\nfollowed by several lines\n indented".
Так что здесь не репрезентативный анализ ...
Простые API "формата и печати"
Повторно реализованный Эдинбург C -Пролог writef/x
По крайней мере реализация в SWI-Prolog (устарела) "writef Семейство " ведет себя снисходительно (не уверен насчет оригинала writef/x
):
?- writef("1: %t 2: %t",[a,b]).
1: a 2: b
true.
?- writef("1: %t 2: %t",[a]).
1: a 2: %t
true.
Java s java.util.Formatter
java .util. Formatter придирчив:
import java.util.*;
class Main {
public static void main(String[] argv) {
Formatter f = new Formatter();
System.out.println(f.format("%s %s %s %s", "a", "b", "c" ));
}
}
Exception in thread "main" java.util.MissingFormatArgumentException: Format specifier '%s'
at java.util.Formatter.format(Formatter.java:2519)
at java.util.Formatter.format(Formatter.java:2455)
at Main.main(Main.java:7)
Похоже, проверка спецификации форматера, хотя и вне языка, но, очевидно, выполнимо в большинстве случаев во время (или, скорее, сразу после) синтаксического анализа (т.е. " строчка printf
строка), это не вещь в 2020 году.
Perl
Старый добрый Ларри знает, как праздновать: Perl 5 (и, вероятно, Perl 6) просто предупреждает о несоответствии количества аргументов и даже просто предупреждает о несоответствии типа аргумента:
#!/usr/bin/perl -w
printf "%d %s %s %s %s\n", "x", "a", "b", "c";
printf "Ok, Buddy!\n";
Argument "x" isn't numeric in printf at test.pl line 3.
Missing argument in printf at test.pl line 3.
0 a b c
Ok, Buddy!
C
#include <stdio.h>
int main(char *argv[],int argc) {
printf("Hello, World! Goodbye, %s");
}
* 1 167 *
Мы находим отсутствие проверки за пределами границ, по крайней мере, в этом сборнике. Должен ли даже номинально «низкоуровневый» (... , но на самом деле ) действительно допустить это в 2020 году?
Протоколирование API и библиотек
Java ведение журнала с использованием " SLF4J over Logback "
- SLF4J - это" Фасад простого ведения журнала для Java "- тонкий API, который может быть реализован с помощью одной из" реализаций регистрации ", которая выполняется путем загрузки необходимой библиотеки при инициализации программы. По умолчанию он ничего не регистрирует.
- Logback - это реализация ведения журнала, реализующая API SLF4J. Просто сделайте его JAR-файл доступным во время выполнения.
В файле с именем MainSlf4j.java
:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MainSlf4j {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(MainSlf4j.class);
String[] args1 = {"sweet", "you"};
String[] args2 = {"forgetful"};
logger.info("Hello, World. This is {} of {}", (Object[])args1);
logger.info("Goodbye, World. This is {} of {}", (Object[])args2);
}
}
Скомпилируйте и запустите в bash:
cd jars
wget "https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar"
wget "https://repo1.maven.org/maven2/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar"
wget "https://repo1.maven.org/maven2/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar"
cd code
export CLASSPATH=
CLASSPATH=$CLASSPATH:jars/logback-classic-1.2.3.jar
CLASSPATH=$CLASSPATH:jars/logback-core-1.2.3.jar
CLASSPATH=$CLASSPATH:jars/slf4j-api-1.7.30.jar
javac MainSlf4j.java && java MainSlf4j
09:28:24.355 [main] INFO MainSlf4j - Hello, World. This is sweet of you
09:28:24.360 [main] INFO MainSlf4j - Goodbye, World. This is forgetful of {}
Да, это снисходительно. Это, конечно, можно изменить - вы сохраняете тот же API SLF4J, но заменяете реализацию, Logback, чем-то более строгим во время запуска процесса (это делается путем замены файла JAR Logback вашим собственным).
Java '* java.util.logging
Готовые java .util.logging средства ведения журнала на самом деле не имеют возможностей форматирования и требуют, чтобы вы строили строку messgae некоторыми другие средства.
Так - не применимо.
Библиотеки шаблонов
Java на основе шаблонов Terence Parr "StringTemplate"
Основной сайт StringTemplate и Введение в StringTemplate .
Шаблонирует инструмент для использования на go, когда кто-то пытается что-то, что все еще может быть решено энергичным взломом с использованием printf
и его потомков.
Этот код трудоемок из-за трудоемкого синтаксиса Java. Вероятно, следует сделать это в Groovy.
В файле с именем MainST.java
:
import org.stringtemplate.v4.*;
class MyData {
public final String name;
public final int id;
MyData(String name, int id) {
this.name = name; this.id = id;
}
}
class MainST {
public static void main(String[] argv) {
{
ST st = new ST("Hello, $x.name$; your number is $x.id$", '$', '$');
st.add("x", new MyData("World", 1729));
System.out.println("Good: " + st.render());
}
{
System.out.println("Missing member 'name'...");
ST st = new ST("Hello, $x.name$; your number is $x.otherid$", '$', '$');
st.add("x", new MyData("World", 1729));
System.out.println(st.render());
}
{
System.out.println("Missing attribute 'x'...");
ST st = new ST("Hello, $x.name$; your number is $x.id$", '$', '$');
System.out.println(st.render());
}
}
}
Скомпилировать и запустить в bash:
cd jars
wget "https://www.stringtemplate.org/download/ST-4.3.jar"
wget "https://www.antlr.org/download/antlr-4.8-complete.jar"
cd code
export CLASSPATH=
CLASSPATH=$CLASSPATH:jars/antlr-4.8-complete.jar
CLASSPATH=$CLASSPATH:jars/ST-4.3.jar
javac MainST.java && java MainST
Good: Hello, World; your number is 1729
Missing member 'name'...
Hello, World; your number is
Missing attribute 'x'...
context [anonymous] 1:8 attribute x isn't defined
context [anonymous] 1:33 attribute x isn't defined
Hello, ; your number is
Мягкий тоже. Не уверен, что это настраивается.