nhầm lẫn về danh sách chứa trong một tổng hợp, có thể vấn đề bối cảnh?


8

Phiên bản Rakudo 2020.01

Tôi đã viết một số mã vứt đi và không bận tâm đến việc thực hiện một lớp, chỉ sử dụng Hash như một công việc giống nhau. Tôi tìm thấy một số hành vi đáng ngạc nhiên với danh sách.

class Q1 {}
class R1 {
    has Str $.some-str is required;
    has @.some-list is required;
}

my $r1 = R1.new(
    some-str => '…',
    some-list => (Q1.new, Q1.new, Q1.new)
);

# hash as poor man's class
my $r2 = {
    some-str => '…',
    some-list => (Q1.new, Q1.new, Q1.new)
};

multi sub frob(R1 $r1) {
    for #`(Array) $r1.some-list -> $elem {
        $elem.raku.say;
    }
}

multi sub frob(Hash $r2) {
    for #`(List) $r2<some-list> -> $elem {
        $elem.raku.say;
    }
}

frob $r1;
# OK.
# Q1.new
# Q1.new
# Q1.new

frob $r2;
# got:
# (Q1.new, Q1.new, Q1.new)

# expected:
# Q1.new
# Q1.new
# Q1.new

frob(Hash …)hoạt động như mong đợi khi tôi gọi .flathoặc .listtrong danh sách (mặc dù nó đã là một danh sách‽).

Tôi đã cố gắng tạo ra một trường hợp thử nghiệm tối thiểu, nhưng điều này hoạt động giống hệt AFAICT.

for [Q1.new, Q1.new, Q1.new] -> $elem {
    $elem.raku.say;
}

for (Q1.new, Q1.new, Q1.new) -> $elem {
    $elem.raku.say;
}

Tôi đã đọc tài liệu về Danh sách và Vô hướng nhiều lần, nhưng tôi vẫn không thể hiểu được quan sát của mình. Tại sao tôi phải xử lý đặc biệt danh sách trong Hash, nhưng không phải trong lớp?


Tôi nhận được No such method 'raku' for invocant of type 'Q1'khi tôi cố chạy nó
Håkon Hægland

2
@ Håkon Hægland: nó được gọi là .perl: có lẽ Rakudo của bạn không đủ hiện tại?
Elizabeth Mattijsen

Câu trả lời:


10

for không lặp trên các giá trị được ghi thành từng khoản.

Khi bạn đặt một cái gì đó vào một thùng chứa vô hướng, nó sẽ được chia thành từng mục.

sub foo ( $v ) { # itemized
  for $v { .say }
}
sub bar ( \v ) {
  for v { .say }
}

foo (1,2,3);
# (1 2 3)

bar (1,2,3);
# 1
# 2
# 3

Một phần tử trong Hash cũng là một thùng chứa vô hướng.

my %h = 'foo' => 'bar';

say %h<foo>.VAR.^name;
# Scalar

Vì vậy, nếu bạn đặt một danh sách vào Hash, nó sẽ được chia thành từng mục.

my %h;

my \list = (1,2,3);
%h<list> = list;

say list.VAR.^name;
# List
say %h<list>.VAR.^name;
# Scalar

Vì vậy, nếu bạn muốn lặp lại các giá trị, bạn phải hủy mục hóa nó.

%h<list>[]
%h<list><>
%h<list>.list
%h<list>.self

@(%h<list>)

given %h<list> -> @list {  }

my @list := %h<list>;

(my @ := %h<list>)  # inline version of previous example

Bạn có thể tránh thùng chứa vô hướng này bằng cách ràng buộc thay thế.

%h<list> := list;

(Điều này ngăn người =vận hành làm việc trên phần tử băm đó.)


Nếu bạn nhận thấy rằng trong đối tượng lớp bạn đã định nghĩa nó @không$

class R1 {
    has Str $.some-str is required;
    has @.some-list is required;
}

Nếu bạn thay đổi nó thành một $và đánh dấu nó, rwnó sẽ hoạt động như ví dụ Hash

class R2 {
    has Str $.some-str is required;
    has List $.some-list is required is rw;
}

my $r2 = R2.new(
    some-str => '…',
    some-list => (1,2,3),
);

for $r2.some-list { .say }
# (1 2 3)

Nó phải là một $biến hoặc nó sẽ không nằm trong một thùng chứa vô hướng.
Nó cũng phải được đánh dấu rwđể người truy cập trả về thùng chứa vô hướng thực tế chứ không phải là giá trị khử mục.


6

Điều này không có gì để làm với []so với (). Điều này có liên quan đến sự khác biệt giữa $(chỉ ra một mặt hàng) và %(chỉ ra một Hiệp hội):

sub a(%h) { dd %h }       # a sub taking an Associative
sub b(Hash $h) { dd $h }  # a sub taking an item of type Hash

a { a => 42 };  # Hash % = {:a(42)}
b { a => 42 };  # ${:a(42)}

Trong trường hợp "b", những gì nhận được là một mục . Nếu bạn cố gắng lặp lại điều đó, bạn sẽ nhận được 1 lần lặp cho mục đó. Trong trường hợp "a", bạn đã chỉ ra rằng đó là thứ gì đó mà bạn muốn (với %sigil).

Có lẽ một ví dụ rõ ràng hơn:

my $a = (1,2,3);
for $a { dd $_ }  # List $a = $(1, 2, 3)␤

$alà một mục, bạn nhận được một lần lặp. Bạn có thể chỉ ra rằng bạn muốn lặp lại điều cơ bản, bằng cách thêm .list:

for $a.list { dd $_ }  # 1␤2␤3␤

Hoặc, nếu bạn muốn nhận thêm linenoisy, tiền tố a @:

for @$a { dd $_ }  # 1␤2␤3␤

1
Nhưng trong frob multi sub, tôi không lặp lại một hàm băm hoặc vật phẩm, tôi đang lặp lại một mảng hoặc danh sách được truy cập bởi thuộc tính của lớp hoặc thành viên của hàm băm. Tôi đã xác minh các loại và chú thích chúng ở nơi thích hợp với các bình luận được gạch chân.
daxim

5

Không hoàn toàn là một câu trả lời, nhưng là một quan sát: trong Raku, nó trả tiền để sử dụng các lớp thay vì băm, trái với Perl:

my %h = a => 42, b => 666;
for ^10000000 { my $a = %h<a> }
say now - INIT now;  # 0.4434793

Sử dụng các lớp và các đối tượng:

class A { has $.a; has $.b }
my $h = A.new(a => 42, b => 666);
for ^10000000 { my $a = $h.a }
say now - INIT now;  # 0.368659

Không chỉ sử dụng các lớp nhanh hơn, nó còn ngăn bạn tạo lỗi chính tả khi khởi tạo nếu bạn thêm is requiredđặc điểm:

class A { has $.a is required; has $.b is required }
A.new(a => 42, B => 666);
# The attribute '$!b' is required, but you did not provide a value for it.

Và nó ngăn bạn mắc lỗi chính tả khi truy cập nó:

my $a = A.new(a => 42, b => 666);
$a.bb;
# No such method 'bb' for invocant of type 'A'. Did you mean 'b'?

1
Cảm ơn bạn đã cho tôi một cái gì đó để liên kết đến trong một tài liệu tôi đang viết! Tôi biết ai đó đã nói rằng lớp + thuộc tính> băm để tăng tốc trở lại nhưng tôi không thể tìm thấy nó. Tôi hiện đang làm lại mô-đun CLDR để sử dụng các thuộc tính bất cứ khi nào có bản sao lưu băm (thân thiện với người dùng một cách đáng ngạc nhiên FALLBACK)
user0721090601
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.