Какое минимальное изменение в моем коде позволит сохранить логическую чистоту?

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

% minset_one(1 in D1, 1 in D2, D1, D2, D1Len, D2Len, T).
minset_one_(true,  false, D1, _,  _,     _,     D1).
minset_one_(false, true,  _,  D2, _,     _,     D2).
minset_one_(true,  true,  _,  D2, D1Len, D2Len, D2) :- D1Len >= D2Len.
minset_one_(true,  true,  D1, _,  D1Len, D2Len, D1) :- D1Len < D2Len.

minset_one(D1, D2, T) :-
    (member(1, D1) -> D1check = true ; D1check = false),
    (member(1, D2) -> D2check = true ; D2check = false),
    length(D1, D1Len),
    length(D2, D2Len),
    
    minset_one_(D1check, D2check, D1, D2, D1Len, D2Len, T).

например

?- D1 = [X,Y,Z], D2 = [U,V], minset_one(D1,D2,T).

D1 = [1, Y, Z],
D2 = T, T = [1, V],
U = X, X = 1 ;

false

возможны и другие решения. member(1, D1) не возвращается через [1, Y, Z], затем [X, 1, Z], затем [X, Y, 1].

Стоит ли изучать 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
0
45
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я думаю, это было бы:

добавлять:

:- use_module(library(reif)).

... и заменить:

    %(member(1, D1) -> D1check = true ; D1check = false),
    %(member(1, D2) -> D2check = true ; D2check = false),
    memberd_t(1, D1, D1check),
    memberd_t(1, D2, D2check),

Пример разницы между member и memberd_t:

?- member(X, [A, B, C]).
X = A ;
X = B ;
X = C.

?- memberd_t(X, [A, B, C], IsMember).
X = A,
IsMember = true ;
X = B,
IsMember = true,
dif (A,B) ;
X = C,
IsMember = true,
dif (A,C),
dif (B,C) ;
IsMember = false,
dif (A,X),
dif (B,X),
dif (C,X).

?- memberd_t(X, [A, B, C], IsMember), X = 5, A = 5, C = 5.
X = A, A = C, C = 5,
IsMember = true ;
false.

Таким образом, memberd_t сам добавляет ограничения dif/2. Чтобы немного повысить производительность, он проходит по списку только один раз.

Определение memberd_t находится, например, в. https://github.com/meditans/reif/blob/master/prolog/reif.pl#L194 и https://www.swi-prolog.org/pack/file_details/reif/prolog/reif.pl?show=src

Действительно очень умный :)

repeat 04.04.2022 21:55

Спасибо; это ответ, он работает и отвечает на то, что я на самом деле спрашивал, в том, что это минимальное изменение, которое делает его логически чистым, так что голосуйте. Но если бы это был вопрос «Как исправить мой код для рисования эллипса, чтобы он мог рисовать идеальные круги?» Я думаю, что «Использовать библиотеку рисования с функцией круга» не будет тем ответом, которого я желал. Я действительно хотел знать, что не так с моей попыткой овеществления с помощью -> и как я могу написать немного лучше.

TessellatingHeckler 05.04.2022 00:59

Я добавил еще немного пояснений. Объяснение @repeat, конечно, лучше моего :-)

brebs 05.04.2022 14:49
Ответ принят как подходящий

Проблема с (->)/2 (и друзьями)

Рассмотрим следующую цель:

(member(1,D1) -> D1check = true ; D1check = false)

(->)/2 фиксирует первый ответ member(1,D1) — остальные ответы не учитываются.

Могут ли нам здесь помочь альтернативы (->)/2, такие как (*->)/2 (SWI, GNU) или if/3 (SICStus)?

Нет. Эти делать не игнорируют альтернативные ответы, чтобы сделать member(1,D1) успешным, но они не считают, что логическое отрицание member(1,D1) могло быть успешным также.

Вернемся к основам: «Если P, то Q, иначе R» ≡ «(P ∧ Q) ∨ (¬P ∧ R)».

Итак, давайте перепишем (If -> Then ; Else) как (If, Then ; Not_If, Else):

(member(1,D1), D1check = true ; non_member(1,D1), D1check = false)

Как мы должны реализовать non_member(X,Xs) — можем ли мы просто написать \+ member(X,Xs)?

Нет! Чтобы сохранить логическую чистоту, нам лучше не основываться на «отрицании как конечной неудаче».

К счастью, сочетание maplist/2 и dif/2 работает здесь:

non_member(X,Xs) :-
   maplist(dif (X),Xs).

Собираем все вместе

Итак, вот минимальное изменение, которое я предлагаю:

minset_one_(true,  false, D1, _,  _,     _,     D1).
minset_one_(false, true,  _,  D2, _,     _,     D2).
minset_one_(true,  true,  _,  D2, D1Len, D2Len, D2) :- D1Len >= D2Len.
minset_one_(true,  true,  D1, _,  D1Len, D2Len, D1) :- D1Len < D2Len.

non_member(X,Xs) :- 
   maplist(dif (X),Xs).

minset_one(D1, D2, T) :-
   (member(1,D1), D1check = true ; non_member(1,D1), D1check = false),
   (member(1,D2), D2check = true ; non_member(1,D2), D2check = false),
   length(D1, D1Len),
   length(D2, D2Len),
   minset_one_(D1check, D2check, D1, D2, D1Len, D2Len, T).

Выполнив пример запроса, мы теперь получаем:

?- D1 = [X,Y,Z], D2 = [U,V], minset_one(D1,D2,T).
   D1 = [1,Y,Z], X = U, U = 1, D2 = T, T = [1,V]
;  D1 = [1,Y,Z], X = V, V = 1, D2 = T, T = [U,1]
;  D1 = T, T = [1,Y,Z], X = 1, D2 = [U,V], dif (U,1), dif (V,1)
;  D1 = [X,1,Z], Y = U, U = 1, D2 = T, T = [1,V]
;  D1 = [X,1,Z], Y = V, V = 1, D2 = T, T = [U,1]
;  D1 = T, T = [X,1,Z], Y = 1, D2 = [U,V], dif (U,1), dif (V,1)
;  D1 = [X,Y,1], Z = U, U = 1, D2 = T, T = [1,V]
;  D1 = [X,Y,1], Z = V, V = 1, D2 = T, T = [U,1]
;  D1 = T, T = [X,Y,1], Z = 1, D2 = [U,V], dif (U,1), dif (V,1)
;  D1 = [X,Y,Z], D2 = T, T = [1,V], U = 1, dif (X,1), dif (Y,1), dif (Z,1)
;  D1 = [X,Y,Z], D2 = T, T = [U,1], V = 1, dif (X,1), dif (Y,1), dif (Z,1)
;  false.

Лучше. Конечно, мне кажется, что ничего не пропало.

Я понимаю; и я кажется понял, спасибо :). (Он должен пройти по списку дважды, один раз, чтобы member/2 завершился неудачно, и еще раз, чтобы non_member завершился успешно?)

TessellatingHeckler 05.04.2022 01:16

Да, при таком подходе необходимо дважды просмотреть список; memberd_t/3 снижает эту стоимость, но делает больше: if помогает сохранить небольшое количество точек выбора, покрывая при этом максимально общие случаи.

repeat 05.04.2022 12:36

Я, конечно, чувствую, что здесь скрываются интересные вопросы: 1 как показать, что одна чистая реализация дает избыточные ответы на конкретный запрос, а другая нет, 2 как избавиться от этих избыточных ответов.

repeat 05.04.2022 12:48

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