2011-12-04 25 views
7

Thông báo sử dụng cho Set nhắc nhở chúng tôi rằng nhiều phép gán có thể dễ dàng được thực hiện trên hai danh sách mà không cần phải tách riêng bất kỳ thứ gì. Ví dụ:Lỗi khi tạo biến được bản địa hóa (làm hằng số)

Remove[x1, x2, y1, y2, z1, z2]; 
{x1, x2} = {a, b} 

Thực hiện sự phân công và lợi nhuận:

{a, b} 

Thread, thường được sử dụng để tạo ra danh sách các quy tắc, cũng có thể được gọi một cách rõ ràng để đạt được kết quả tương tự:

Thread[{y1, y2} = {a, b}] 
Thread[{z1, z2} -> {a, b}] 

Cung cấp:

{a, b} 
{z1 -> a, z2 -> b} 

Tuy nhiên, việc sử dụng phương pháp này để tạo ra các hằng số được bản địa hóa tạo ra một lỗi. Hãy xem xét ví dụ chức năng này tầm thường:

Remove[f]; 
f[x_] := 
With[{{x1, x2} = {a, b}}, 
    x + x1 + x2 
    ] 
f[z] 

đây thông báo lỗi:

With::lvset: "Local variable specification {{x1,x2}={a,b}} contains 
{x1,x2}={a,b}, which is an assignment to {x1,x2}; only assignments 
to symbols are allowed." 

Các tài liệu thông báo lỗi (ref/message/With/lvw), nói trong 'Thông tin' phần đó, "Thông báo này được tạo ra khi phần tử đầu tiên trong Với không phải là danh sách các bài tập cho các ký hiệu. " Với lời giải thích này, tôi hiểu cơ chế của việc phân công của tôi thất bại. Tuy nhiên, tôi đang bối rối và tự hỏi liệu đây có phải là hạn chế cần thiết bởi WRI hay một sự giám sát thiết kế nhỏ cần được báo cáo.

Vì vậy, đây là câu hỏi của tôi:

bất cứ ai có thể làm sáng tỏ về hành vi này và/hoặc cung cấp một workaround? Tôi đã thử nghiệm với lực lượng cố gắng để buộc Evaluation, mà không có may mắn, và tôi không chắc chắn những gì khác để thử.

+1

Tôi thường cân nhắc các lựa chọn thiết kế như vậy và thỉnh thoảng tôi đăng các câu hỏi như vậy ở đây. Thật không may, hiếm khi những điều đó được giải thích. Tôi đã tự bào chữa với thực tế là With/Block/Module chỉ hoạt động khác nhau và 'Set' trong đối số đầu tiên không thực sự thực hiện thao tác' Set' nhưng thay vào đó là cú pháp. –

+0

Từ việc đọc tài liệu thông báo lỗi (phần được trích dẫn ở cuối Q), có vẻ như tôi có một số mẫu đơn giản khớp với hành vi hiện tại --- nghĩa là chấp nhận các mẫu có Danh sách đầu, chứa chỉ các phần tử đánh giá thành True cho một thứ như MatchQ [Giữ [Set [x, 1]], Giữ [Đặt [Symbol_, _]]]. Nếu đây là trường hợp, ít nhất là trong thế giới nhỏ cận thị của tôi, dường như có thể tinh chỉnh mô hình phù hợp/thử nghiệm để cho phép hành vi tôi tìm kiếm, và tài liệu trên Set dường như ngụ ý sẽ hoạt động (dưới dạng biểu mẫu hợp lệ của bài tập). – telefunkenvf14

+0

Tôi không đồng ý, nhưng tôi biết không có cách nào để thực hiện thay đổi đó. –

Trả lời

11

Điều bạn yêu cầu là khó khăn. Đây là một công việc cho các macro, như đã được những người khác tiếp xúc. Tôi sẽ khám phá một khả năng khác nhau - để sử dụng các biểu tượng giống nhau nhưng đặt một số hàm bao quanh mã bạn muốn viết. Ưu điểm của kỹ thuật này là mã được chuyển đổi "lexically" và tại "compile-time", thay vì ở thời gian chạy (như trong các câu trả lời khác). Điều này thường nhanh hơn và dễ gỡ lỗi hơn.

Vì vậy, đây là một chức năng mà sẽ chuyển đổi With với cú pháp đề xuất của bạn:

Clear[expandWith]; 
expandWith[heldCode_Hold] := 
Module[{with}, 
    heldCode /. With -> with //. { 
     HoldPattern[with[{{} = {}, rest___}, body_]] :> 
       with[{rest}, body], 
     HoldPattern[ 
     with[{ 
      Set[{var_Symbol, otherVars___Symbol}, {val_, otherVals___}], rest___}, 
      body_]] :> 
       with[{{otherVars} = {otherVals}, var = val, rest}, body] 
    } /. with -> With] 

Lưu ý rằng điều này hoạt động trên mã được tổ chức. Điều này có lợi thế là chúng ta không phải lo lắng về việc đánh giá khả thi o mã không phải lúc đầu hoặc khi expandWith hoàn tất. Dưới đây là cách hoạt động:

In[46]:= [email protected][With[{{x1,x2,x3}={a,b,c}},x+x1+x2+x3]] 
Out[46]= Hold[With[{x3=c,x2=b,x1=a},x+x1+x2+x3]] 

Điều này, tuy nhiên, không thuận tiện khi sử dụng.Đây là một chức năng thuận tiện để đơn giản hóa này:

ew = Function[code, [email protected]@[email protected], HoldAll] 

Chúng ta có thể sử dụng nó bây giờ như:

In[47]:= [email protected][{{x1,x2}={a,b}},x+x1+x2] 
Out[47]= a+b+x 

Vì vậy, để thực hiện việc mở rộng xảy ra trong các mã, chỉ cần quấn ew xung quanh nó. Đây là trường hợp của bạn cho định nghĩa của hàm:

Remove[f]; 
ew[f[x_] := With[{{x1, x2} = {a, b}}, x + x1 + x2]] 

Bây giờ chúng ta kiểm tra và thấy rằng những gì chúng ta nhận được là một định nghĩa mở rộng:

?f 
Global`f 
f[x_]:=With[{x2=b,x1=a},x+x1+x2] 

Ưu điểm của phương pháp này là bạn có thể quấn ew xung quanh một tùy ý đoạn mã lớn của bạn. Điều gì xảy ra là đầu tiên, mã mở rộng được tạo ra từ nó, như thể bạn sẽ tự viết nó, và sau đó mã đó được thực hiện. Đối với trường hợp các định nghĩa của hàm, như là f ở trên, chúng ta có thể tin rằng việc tạo mã xảy ra tại "thời gian biên dịch", vì vậy bạn tránh được bất kỳ thời gian chạy nào khi sử dụng hàm này sau này, có thể đáng kể nếu hàm này được gọi thường xuyên.

Một ưu điểm khác của phương pháp này là tính tương thích của nó: bạn có thể đưa ra nhiều cú pháp mở rộng, và cho mỗi người trong số họ viết một hàm tương tự như ew. Sau đó, với điều kiện các hàm chuyển đổi mã tùy chỉnh này không giao dịch với nhau, bạn có thể chỉ cần soạn (lồng) chúng, để có được hiệu ứng tích lũy. Theo cách này, bạn tạo một trình tạo mã tùy chỉnh để tạo ra mã Mathematica hợp lệ từ một số biểu thức Mathematica biểu diễn các chương trình trong sự hao mòn tùy chỉnh của bạn, mà bạn có thể tạo trong Mathematica bằng các phương tiện này.

EDIT

Trong văn bản expandWith, tôi sử dụng ứng dụng quy tắc lặp đi lặp lại để tránh đối phó với sự kiểm soát đánh giá, có thể là một mớ hỗn độn. Tuy nhiên, đối với những người quan tâm, đây là một phiên bản thực hiện một số công việc rõ ràng với các đoạn mã chưa được đánh giá.

Clear[expandWithAlt]; 
expandWithAlt[heldCode_Hold] := 
Module[{myHold}, 
    SetAttributes[myHold, HoldAll]; 
    heldCode //. HoldPattern[With[{Set[{vars__}, {vals__}]}, body_]] :> 
    With[{eval = 
       (Thread[Unevaluated[Hold[vars] = Hold[vals]], Hold] /. 
        Hold[decl___] :> myHold[With[{decl}, body]])}, 
     eval /; True] //. myHold[x_] :> x] 

Tôi thấy nó phức tạp hơn nhiều so với lần đầu tiên.

+0

Thật tuyệt vời, Leonid! –

+0

Điều này đến gần với lý tưởng của việc sử dụng 'Với' chính nó, nhưng không sửa đổi chức năng mặc định. Dễ dàng +1. –

+0

Bạn có nghĩ rằng điều này có thể kết hợp với một [Gayley-Villegas] (http://stackoverflow.com/a/5149656/421225) sửa đổi kiểu tích hợp 'Với'? – Simon

6

Các hướng dẫn "LocalConstants" nói

Cách Với [{x = subscript [x, 0], ...}, cơ thể] làm việc là để có cơ thể, và thay thế mỗi xảy ra x , v.v. trong nó bằng Chỉ số [x, 0], v.v. Bạn có thể nghĩ đến Với tổng quan là của /. toán tử, phù hợp để áp dụng cho mã Mathematica thay vì các biểu thức khác.

Đề cập đến lời giải thích này có vẻ như rõ ràng rằng một cái gì đó giống như

x + x1 + x2 /. {x1, x2} -> {a, b} 

sẽ không làm việc vì nó có thể được dự kiến ​​trong Với ký hiệu.

Giả sử bạn thực sự muốn hack xung quanh vấn đề này. With[] có thuộc tính HoldAll, do đó mọi thứ bạn cung cấp làm tham số đầu tiên sẽ không được đánh giá. Để thực hiện tác vụ gán véc tơ như vậy, bạn sẽ phải tạo

With[{x1=a, x2=b}, ...] 

từ ký hiệu vectơ.Rất tiếc,

Thread[{a, b} = {1, 2}] 

không hoạt động vì đối số của Chủ đề không được tổ chức và bài tập được đánh giá trước khi Chủ đề có thể làm bất cứ điều gì.

Cho phép sửa lỗi này

SetAttributes[myThread, HoldFirst]; 
myThread[Set[a_, b_]] := mySet @@@ Transpose[{a, b}] 

cho

In[31]:= myThread[{a, b, c} = {1, 2, 3}] 
Out[31]= {mySet[a, 1], mySet[b, 2], mySet[c, 3]} 

gì trông đầy hứa hẹn lúc đầu, chỉ cần di chuyển các vấn đề một chút đi. Để sử dụng điều này trong With[], bạn phải thay thế tại một số điểm mySet bằng Bộ thực. Đúng lúc đó, With[] không xem danh sách {a = 1, b = 2, c = 3} nhưng, vì nó đã được đánh giá, kết quả của tất cả các bài tập

In[32]:= With[ 
Evaluate[myThread[{a, b, c} = {1, 2, 3}] /. mySet :> Set], a + b + c] 

During evaluation of In[32]:= With::lvw: Local 
variable specification {1,2,3} contains 1, which is not an assignment to a symbol. >> 

Out[32]= With[{1, 2, 3}, a + b + c] 

Có vẻ là không dễ dàng cách xung quanh điều này và có một câu hỏi thứ hai ở đây: Nếu có một cách xung quanh hạn chế này, là nó nhanh như với sẽ được hoặc làm chúng ta mất lợi thế tốc độ so với Module? Và nếu tốc độ không phải là quá quan trọng, tại sao không sử dụng Module hoặc Block ở nơi đầu tiên?

+0

halirutan, chào mừng vào StackOverflow. –

+0

Đoạn cuối cùng của bạn xuất hiện để gợi ý rằng vấn đề này không ảnh hưởng đến 'Mô-đun' hoặc' Chặn', nhưng nó có tác dụng. Hoặc, bạn đang nói có một workaround cho 'Module' hoặc' Block' mà không hoạt động trên 'With'? –

+2

@Mr.Wizard Trong Module và Block bạn có thể thực hiện ít nhất Module [{a, b, c}, {a, b, c} = {1, 2, 3}; a + b + c] không hoạt động trong Với. – halirutan

7

Vấn đề phức tạp là giữ đối số đầu tiên của Đặt chưa được đánh giá. Dưới đây là gợi ý của tôi (mở cửa cho những cải tiến tất nhiên):

SetAttributes[myWith, HoldAll]; 
    myWith[{s : Set[a_List, b_List]}, body_] := 
    [email protected] 
     Hold[With][ 
     Table[Hold[Set][Extract[Hold[s], {1, 1, i}, Hold], 
     Extract[Hold[s], {1, 2, i}]], {i, [email protected]}], [email protected]] 
    x1 = 12; 
    Remove[f]; 
    f[x_] := myWith[{{x1, x2} = {a, b}}, x + x1 + x2] 
    f[z] 

kết quả trong

a+b+z 

Lấy cảm hứng từ halirutan dưới đây tôi nghĩ rằng giải pháp của mình, làm nhẹ hơn một cách an toàn, tương đương với trên:

SetAttributes[myWith, HoldAll]; 
myWith[{Set[a : {__Symbol}, b_List]} /; Length[a] == Length[b], 
    body_] := 
[email protected] 
    Hold[With][ 
    Replace[Thread[Hold[a, b]], Hold[x_, y_] :> Hold[Set[x, y]], 1], 
    [email protected]] 
+0

Tuyệt vời! Nắm giữ 'với' có vẻ là điều quan trọng. Sau đây sử dụng cùng một thủ thuật: 'f [x_] = ReleaseHold @ Giữ [Với] [ Flatten @ {({x1, x2} -> {a, b} // Chủ đề) /. (Quy tắc [q_, r_] -> HoldForm @ Đặt [q, r])}, x + x1 + x2] 'Sử dụng' x1 = 5; f [z] 'cho' a + b + z'. – kglr

+0

Nếu bạn thay thế đối số đầu tiên '{s: Đặt [a_List, b_List]' bằng 's: (Set | Rule) [a_List, b_List]' mà không thay đổi gì khác, thì người ta có thể sử dụng cả hai là 'g [x_] : = myVới [{{x1, x2) -> {a, b}}, x + x1 + x2] 'sử dụng các quy tắc và như' h [x _]: = myWith [{{x1, x2) = {a, b }}, x + x1 + x2] 'dùng phép gán để khởi tạo biến cục bộ. – kglr

+0

Tốt thứ Rolf! Xin lỗi tôi mới quay lại để bình luận và bình chọn. +1. –

3

Bạn có thể sử dụng Transpose để rút ngắn giải pháp Rolfs bằng 100 ký tự:

SetAttributes[myWith, HoldAll]; 
myWith[{Set[a_List, b_List]}, body_] := 
ReleaseHold[Hold[With][Hold[Set[#1, #2]] & @@@ Transpose[{a, b}], 
    [email protected] 
    ]] 

@ Giống như vậy, yep các ngắt ở trên nếu một trong hai biến đã có giá trị. Điều gì về điều này:

SetAttributes[myWith, HoldAll]; 
myWith[{Set[a_List, b_List]}, body_] := 
[email protected] 
    Hold[With][Thread[Hold[a, b]] /. Hold[p__] :> Hold[Set[p]], 
    [email protected]] 
+1

Điều này xảy ra khi một trong các biến trong 'a' đã được đặt trước đó, ví dụ: 'a = 3; myWith [{{a, b, c} = {1, 2, 3}},^2 + b] 'đưa ra một lỗi, nhưng điều này dễ dàng được sửa bằng cách đặt' Block [{a}, ...] ' xung quanh phía bên phải của 'myWith'. – Heike

+0

Tôi đã thấy nó sau khi đăng câu trả lời. – halirutan

Các vấn đề liên quan