2011-12-15 33 views
7

Câu hỏi này là về vấn đề đi qua bằng cách tham khảo tại M (một câu hỏi có liên quan của tôi là ở đây simple question on passing data between functions)Sử dụng một cấu trúc để vượt qua các thông số bằng cách tham khảo

Trong khi tôi đang cố gắng tìm một cách để vượt qua mọi thứ bằng cách tham khảo mà không sử dụng Unevaluted[] hoặc HoldFirst[], tôi nhấn vào phương pháp này do nhầm lẫn và nó trông thực sự làm việc tốt cho tôi, mặc dù tôi không hiểu nó hoạt động như thế nào và bất kỳ rủi ro tiềm ẩn nào khi sử dụng nó. Vì vậy, tôi muốn giới thiệu cho các chuyên gia ở đây và hỏi họ có cho rằng nó an toàn không (tôi có bản demo rất lớn và cần phải đóng gói các thông số vào các cấu trúc khác nhau để giúp quản lý chúng, và đây là cách tôi tìm thấy trong khi tôi đang cố gắng).

Đây là phương pháp: Đầu tiên chúng ta biết rằng người ta không thể viết như sau:

Remove[p] 
foo[p_] := Module[{u}, 
    u = Table[99, {10}]; 
    p = u 
    ]; 

p = 0; 
foo[p]; 

Một cách để để cập nhật 'p' ở trên là thay đổi để gọi để trở thành

foo[[email protected]]; 

Hoặc bằng cách xác định foo[] với HoldFirst.

Nhưng đây là cách tôi tìm thấy, mà không vượt qua bằng cách tham khảo, không một trong những:

tôi đặt tất cả các thông số trong một cấu trúc (Tôi làm anyway này bây giờ), và vượt qua các cấu trúc, và sau đó người ta có thể cập nhật các trường của struct bên foo[] và các bản cập nhật sẽ được phản ánh trong đường trở về từ cuộc gọi chức năng:

Remove[parms] 
foo[parms_] := Module[{u}, 
    u = Table[99, {10}]; 
    parms["p"] = u 
    ]; 

parms["p"] = 0; 
foo[parms]; 

Bây giờ, parms["p"] chứa danh sách mới {99, 99, 99, 99, 99, 99, 99, 99, 99, 99}

S o, các parms đã được ghi đè/cập nhật bên trong foo[] mà không cần tôi phải thông báo cho M để vượt qua parms bằng cách tham chiếu!

Tôi đã thử điều này trong chương trình của mình và tôi không thấy có phản ứng phụ lạ. CDF đã được cập nhật ok mà không có lỗi. Tôi không biết làm thế nào điều này hoạt động, có thể M xử lý tất cả các lĩnh vực bên trong parms trong ví dụ này là toàn cầu? Tuy nhiên, cả hai trường hợp, tôi hài lòng với điều này cho đến nay vì nó cung cấp cho tôi một cách để gói nhiều tham số của tôi vào cấu trúc, và đồng thời tôi có thể làm cập nhật bên trong các chức năng cho cấu trúc.

Nhưng câu hỏi của tôi là: bạn có thấy một vấn đề lớn với phương pháp này không? Làm thế nào nó hoạt động trong nội bộ? Tôi có nghĩa là làm thế nào để M xử lý này đi mà không có tôi làm HoldFirst hoặc Unevaluated? Tôi biết rằng tôi đã mất khả năng để kiểm tra các thông số như trước, nhưng tôi không thể có mọi thứ tôi muốn. Như tôi đã nói, M cần một cấu trúc dựng sẵn thực sự như một phần của ngôn ngữ và được tích hợp vào nó. Nhưng đây là lúc khác để nói về.

Btw, mô phỏng cấu trúc tốt nhất mà tôi thấy cho đến nay là bởi Leonid Shifrin được đăng ở cuối chủ đề này here nhưng tiếc là tôi không thể sử dụng nó trong bản demo của mình vì nó sử dụng ký hiệu không được phép trong bản trình diễn CDF.

nhờ

Cập nhật: Btw, dưới đây là những gì tôi nghĩ như thế nào M xử lý này.Đây chỉ là một đoán của tôi: Tôi nghĩ rằng param là một số loại của một bảng tra cứu, và các lĩnh vực của nó chỉ được sử dụng như một chỉ số vào nó (có thể là một bảng băm?) Sau đó, param["p1"] sẽ chứa trong nó địa chỉ (không phải là giá trị) của một vị trí trong vùng có giá trị thực tế là param["p1"] trực tiếp.

Something như thế này:

enter image description here

Vì vậy, khi đi qua param, sau đó bên trong hàm foo[], khi gõ param["p1"]=u nó sẽ gây ra bộ nhớ hiện tại được trỏ đến bởi "p1" sẽ được giải tỏa, và sau đó một bộ nhớ mới được cấp phát từ heap, và trong đó giá trị của u được sao chép vào.

Và vì vậy, khi quay trở lại, đây là lý do tại sao chúng tôi thấy nội dung của param["p1"] đã thay đổi. Nhưng những gì thực sự thay đổi, là nội dung của bộ nhớ được trỏ đến bởi địa chỉ mà param["p1"] đại diện. (Có một cái tên, đó là "p1", và một trường địa chỉ, mà chỉ vào nội dung mà tên "p1" đại diện)

Nhưng sau đó điều này có nghĩa, rằng địa chỉ chính nó, mà "p1" đại diện đã thay đổi, nhưng tra cứu tên chính nó (đó là "p1") đã không thay đổi.

Vì vậy, vì tên chính nó không thay đổi, không cần sử dụng HoldFirst, mặc dù dữ liệu được chỉ ra bởi những gì tên này đại diện đã được sửa đổi?

Cập nhật:

Có một trục trặc nhỏ trong phương pháp này, nhưng nó không phải là một vấn đề lớn để làm việc xung quanh nó: khi đi qua những thứ sử dụng phương pháp đối tượng được lập chỉ mục này, nó chỉ ra rằng người ta có thể không sửa đổi một phần của đối tượng. Ví dụ, dưới đây không làm việc:

foo[param_] := Module[{}, 
    param["u"][[3]] = 99 (*trying to update PART of u *) 
    ]; 

param["u"] = Table[0, {5}]; 
foo[param]; 

Trên đây cung cấp cho các lỗi

Set::setps: "param[u] in the part assignment is not a symbol" 

Nhưng cách giải quyết rất dễ dàng. tạo một bản sao cục bộ của toàn bộ lĩnh vực mà ai muốn cập nhật một phần của nó, sau đó cập nhật (phần) của bản sao cục bộ, sau đó viết các bản sao trở lại lĩnh vực này, như

foo[param_] := Module[{u = param["u"]}, (* copy the whole field *) 
    u[[3]] = 99; (*update local copy *) 
    param["u"] = u (*now update the field, ok *) 
    ]; 

param["u"] = Table[0, {5}]; 
foo[param]; 

Vâng điều này. Sẽ tốt hơn nếu người ta có thể cập nhật một phần của trường, vì vậy không cần xử lý 'đặc biệt'. Nhưng ít nhất công việc xung quanh không phải là xấu.

Cập nhật Để hoàn chỉnh, tôi nghĩ tôi đề cập đến một điều nhỏ nhặt khác khi sử dụng các đối tượng được lập chỉ mục và làm việc xung quanh.

tôi đã viết

param[u] = {1, 2, 3} 
param[u][[1 ;; -1]] 

nào trả {1,2,3} như mong đợi.

Sau đó, tôi thấy rằng tôi có thể sử dụng [email protected] thay vì param[u], thực sự giúp quá nhiều dấu ngoặc cứng đang bắt đầu khiến tôi đau đầu.

Nhưng sau đó khi tôi gõ

[email protected][[1 ;; -1]] 

mong đợi để lấy lại câu trả lời giống như trước, nó đã làm không. một lỗi, (tôi nghĩ rằng tôi biết tại sao, vấn đề ưu tiên vận hành, nhưng đó không phải là vấn đề bây giờ), chỉ muốn nói rằng cách giải quyết dễ dàng, hoặc có thể sử dụng param[u][[1 ;; -1]] hoặc sử dụng ([email protected])[[1 ;; -1]]

Tôi thích ([email protected])[[1 ;; -1]] nhiều hơn, vì vậy đó là những gì tôi đang sử dụng ngay bây giờ để truy cập danh sách bên trong các đối tượng được lập chỉ mục.

Ký hiệu [email protected] gần như tôi có thể nhận được ký hiệu bản ghi tiêu chuẩn là param.u vì vậy bây giờ tôi hạnh phúc với các đối tượng được lập chỉ mục và có vẻ như hoạt động thực sự tốt. Chỉ cần một vài điều nhỏ để xem ra cho.

+0

Có rất nhiều điều xảy ra trong câu hỏi này. Bạn có một điều cụ thể mà bạn đang cố gắng đạt được không? –

+0

@MikeBantegui, tôi hỏi về cơ bản nó được triển khai như thế nào, và tại sao nó hoạt động như một khả năng sửa đổi một tham số bên trong một hàm, không được cho phép trong M, như bình thường đi qua trong M là giá trị. Nhưng ở đây nó đang được thực hiện và không sử dụng HoldFirst và không sử dụng Unevaluated. Làm thế nào để giải thích điều này? – Nasser

+0

Nasser, xin vui lòng đọc câu trả lời của tôi và cho tôi biết những gì vẫn chưa rõ ràng. –

Trả lời

5

Tôi sẽ không trả lời câu hỏi về cấu trúc dữ liệu cấp thấp vì tôi đơn giản là không đủ điều kiện để làm như vậy.

Bạn đang tạo "indexed objects" và sử dụng các chuỗi không thay đổi làm chỉ mục. Nói chung tôi tin rằng đây là những tương tự như bảng băm.

Lấy ví dụ mã của bạn, chúng ta hãy loại bỏ một sự mơ hồ có thể bằng cách sử dụng một tên duy nhất cho lập luận của foo:

Remove[parms, foo] 

foo[thing_] := 
    Module[{u}, 
    u = Table[99, {10}]; 
    thing["p"] = u 
    ]; 

parms["p"] = 0; 
foo[parms]; 

parms["p"] 

DownValues[parms] 
{HoldPattern[parms["p"]] :> {99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}

này cho thấy cách các dữ liệu được lưu trữ trong điều kiện của cấp cao Mathematica cấu trúc.

parms là biểu tượng toàn cầu. Như bạn đã quan sát, nó có thể được "truyền" một cách an toàn mà không có bất cứ điều gì xảy ra, bởi vì nó chỉ là một biểu tượng không có OwnValues và không có gì được kích hoạt khi nó được đánh giá. Điều này hoàn toàn giống như việc viết/đánh giá foo của chính nó.

Việc thay thế diễn ra trong foo[thing_] := ... tương tự như With. Nếu chúng ta viết:

With[{thing = parms}, thing[x]] 

thing trong thing[x] được thay thế bằng parms trước thing[x] đánh giá lại. Tương tự như vậy, trong mã ở trên, chúng tôi nhận được parms["p"] = u trước khi thing["p"] hoặc đánh giá Set. Vào thời điểm đó, thuộc tính HoldFirst của số Set sẽ kết thúc và bạn nhận được những gì bạn muốn.

Vì bạn đang sử dụng chuỗi không thay đổi làm chỉ mục của mình nên không có nguy cơ thay đổi. Miễn là bạn biết cách xử lý các ký hiệu không được bản địa hoá như parms thì cũng không có mối nguy hiểm mới nào mà tôi thấy khi sử dụng phương pháp này.

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