Bỏ qua WB (thứ không thực sự cần thiết để trả lời câu hỏi của bạn) - vấn đề dường như có một câu trả lời đơn giản chỉ dựa trên cách các biểu thức được đánh giá trong quá trình làm bài tập. Đây là một ví dụ:
In[1505]:=
notGoodQ[x_]:=True;
Clear[g];
g[x_?notGoodQ]:=(Message[g::nogood,x];Abort[])
In[1509]:= g/:cccQ[g[x0_]]:=True
During evaluation of In[1509]:= g::nogood: -- Message text not found -- (x0_)
Out[1509]= $Aborted
Để làm cho nó hoạt động, tôi đã cố tình tạo định nghĩa cho notGoodQ
để luôn trả về True
. Bây giờ, tại sao lại là g[x0_]
được đánh giá trong quá trình chuyển nhượng thông qua TagSetDelayed
? Câu trả lời là, trong khi TagSetDelayed
(cũng như SetDelayed
) trong một bài tập h/:f[h[elem1,...,elemn]]:=...
không áp dụng bất kỳ quy tắc nào f
có thể có, nó sẽ đánh giá h[elem1,...,elem2]
, cũng như f
. Đây là một ví dụ:
In[1513]:=
ClearAll[h,f];
h[___]:=Print["Evaluated"];
In[1515]:= h/:f[h[1,2]]:=3
During evaluation of In[1515]:= Evaluated
During evaluation of In[1515]:= TagSetDelayed::tagnf: Tag h not found in f[Null]. >>
Out[1515]= $Failed
Thực tế là TagSetDelayed
là HoldAll
không có nghĩa là nó không đánh giá các đối số của nó - nó chỉ có nghĩa là các đối số đến nó không được đánh giá và việc chúng có được đánh giá hay không tùy thuộc vào ngữ nghĩa của TagSetDelayed
(mà tôi đã mô tả ngắn gọn ở trên). Điều tương tự đối với SetDelayed
, vì vậy câu thường được sử dụng rằng nó "không đánh giá các đối số của nó" là không đúng theo nghĩa đen. Một tuyên bố đúng hơn là nó nhận các đối số không được đánh giá và đánh giá chúng theo một cách đặc biệt - không đánh giá r.h.s, trong khi đối với l.h.s, đánh giá head và các phần tử nhưng không áp dụng các quy tắc cho head. Để tránh điều đó, bạn có thể bọc mọi thứ trong HoldPattern
, như thế này:
Clear[g,notGoodQ];
notGoodQ[x_]:=EvenQ[x];
g[x_?notGoodQ]:=(Message[g::nogood,x];Abort[])
g/:cccQ[HoldPattern[g[x0_]]]:=True;
Điều này đi qua. Đây là một số cách sử dụng:
In[1527]:= cccQ[g[1]]
Out[1527]= True
In[1528]:= cccQ[g[2]]
During evaluation of In[1528]:= g::nogood: -- Message text not found -- (2)
Out[1528]= $Aborted
Tuy nhiên, lưu ý rằng sự cần thiết của HoldPattern
bên trong bên tay trái của bạn khi đưa ra định nghĩa thường là một dấu hiệu cho thấy biểu thức bên trong đầu của bạn cũng có thể đánh giá trong quá trình gọi hàm, điều này có thể phá vỡ mã của bạn. Đây là một ví dụ về ý tôi muốn nói:
In[1532]:=
ClearAll[f,h];
f[x_]:=x^2;
f/:h[HoldPattern[f[y_]]]:=y^4;
Mã này cố gắng bắt các trường hợp như h[f[something]]
, nhưng rõ ràng là nó sẽ không thành công vì f[something]
sẽ đánh giá trước khi đánh giá đến h
:
In[1535]:= h[f[5]]
Out[1535]= h[25]
Đối với tôi, nhu cầu về HoldPattern
trên l.h.s. là một dấu hiệu cho thấy tôi cần xem xét lại thiết kế của mình.
CHỈNH SỬA
Về việc gỡ lỗi trong quá trình tải trong WB, một điều bạn có thể làm (IIRC, không thể kiểm tra ngay bây giờ) là sử dụng các câu lệnh in cũ tốt, kết quả của chúng sẽ xuất hiện trong bảng điều khiển của WB. Cá nhân tôi hiếm khi cảm thấy cần trình gỡ lỗi cho mục đích này (gỡ lỗi gói khi tải)
CHỈNH SỬA 2
Đáp lại nội dung chỉnh sửa trong câu hỏi:
Về thứ tự của các định nghĩa:có, bạn có thể làm điều này, và nó giải quyết được vấn đề cụ thể này. Nhưng nói chung, cách này không mạnh, và tôi sẽ không coi nó là một phương pháp chung tốt. Thật khó để đưa ra một lời khuyên rõ ràng cho một trường hợp hiện tại, vì nó hơi xa rời bối cảnh của nó, nhưng đối với tôi, có vẻ như việc sử dụng UpValues
ở đây là không hợp lý. Nếu điều này được thực hiện để xử lý lỗi, thì có cách khác
để làm điều đó mà không cần sử dụng UpValues
.
Nói chung, UpValues
được sử dụng phổ biến nhất để quá tải một số chức năng theo cách an toàn, mà không cần thêm bất kỳ quy tắc nào để chức năng bị quá tải. Một lời khuyên là tránh liên kết UpValues
với các đầu cũng có DownValues
và có thể đánh giá - bằng cách làm này, bạn bắt đầu chơi trò chơi với người đánh giá, và cuối cùng sẽ thua. An toàn nhất là đính kèm UpValues
thành các ký hiệu trơ (đầu, vùng chứa), thường đại diện cho "loại" đối tượng mà bạn muốn nạp chồng cho một hàm nhất định.
Về nhận xét của tôi về sự hiện diện của HoldPattern
chỉ ra một thiết kế xấu. Chắc chắn có có sử dụng hợp pháp cho HoldPattern
, chẳng hạn như cái này (hơi nhân tạo):
In[25]:=
Clear[ff,a,b,c];
ff[HoldPattern[Plus[x__]]]:={x};
ff[a+b+c]
Out[27]= {a,b,c}
Ở đây nó là hợp lý vì trong nhiều trường hợp Plus
vẫn chưa được đánh giá, và hữu ích ở dạng không được đánh giá - vì người ta có thể suy ra rằng nó đại diện cho một tổng. Chúng ta cần HoldPattern
ở đây vì cách Plus
được xác định trên một đối số duy nhất và bởi vì một mẫu xảy ra là một đối số duy nhất (mặc dù nó mô tả chung là nhiều đối số) trong quá trình định nghĩa. Vì vậy, chúng tôi sử dụng HoldPattern
ở đây để ngăn việc coi mẫu là đối số bình thường, nhưng điều này chủ yếu khác với các trường hợp sử dụng dự kiến cho Plus
. Bất cứ khi nào trường hợp này xảy ra (chúng tôi chắc chắn rằng định nghĩa sẽ hoạt động tốt cho các trường hợp sử dụng dự kiến), HoldPattern
Ổn. Lưu ý b.t.w., rằng ví dụ này cũng rất mỏng manh:
In[28]:= ff[Plus[a]]
Out[28]= ff[a]
Lý do tại sao hầu hết nó vẫn OK là bình thường chúng tôi không sử dụng Plus
trên một đối số duy nhất.
Tuy nhiên, có một nhóm trường hợp thứ hai, trong đó cấu trúc của các đối số thường được cung cấp giống với cấu trúc của các mẫu được sử dụng cho định nghĩa. Trong trường hợp này, đánh giá mẫu trong quá trình gán cho biết rằng đánh giá tương tự sẽ xảy ra với các đối số thực tế trong khi gọi hàm. Việc sử dụng của bạn thuộc loại này. Nhận xét của tôi về một lỗ hổng thiết kế dành cho những trường hợp như vậy - bạn có thể ngăn đánh giá mẫu, nhưng bạn cũng sẽ phải ngăn các đối số đánh giá, để làm cho điều này hoạt động. Và việc so khớp mẫu với biểu thức không được đánh giá hoàn toàn là rất mong manh. Ngoài ra, hàm không bao giờ được giả định một số điều kiện bổ sung (ngoài những gì nó có thể nhập-kiểm tra) cho các đối số.