Membatalkan susunan Array


34

Tantangan ini terinspirasi oleh pertanyaan tentang Mathematica.SE .

Katakanlah Anda memiliki daftar / array bersarang dari beberapa struktur arbitrer (daftar di setiap level tidak harus memiliki panjang yang sama). Untuk kesederhanaan, kami akan menganggap bahwa node adalah bilangan bulat non-negatif atau array kosong. Sebagai contoh

[[[1, 3], 2], [1, 4], 12, [[0, [], 0], [5, [7]]]]

Terkadang lebih mudah untuk meratakan daftar itu untuk melakukan manipulasi node, misalnya

--> [1, 3, 2, 1, 4, 12, 0, 0, 5, 7]
--> [1, 1, 0, 1, 0, 0, 0, 0, 1, 1]

Tetapi pada akhirnya Anda benar-benar ingin mempertahankan struktur aslinya, jadi Anda ingin mengubahnya menjadi

--> [[[1, 1], 0], [1, 0], 0, [[0, [], 0], [1, [1]]]

Tugas Anda adalah melakukan langkah terakhir itu.

Diberikan daftar bersarang dari bilangan bulat non-negatif sewenang-wenang, yang mewakili struktur hasil yang diinginkan, dan daftar datar bilangan bulat non-negatif, yang mewakili nilai yang diinginkan, membentuk kembali daftar datar menjadi bentuk daftar terstruktur. Anda dapat mengasumsikan bahwa kedua daftar berisi jumlah bilangan bulat yang sama.

Seperti biasa Anda tidak harus berurusan dengan input yang tidak valid (mis. Daftar kedua tidak datar, input yang cacat secara sintaksis, tidak memiliki integer sebagai node, dll.). Anda dapat memodifikasi array input dalam kode Anda.

Anda dapat menulis suatu fungsi atau program, mengambil input melalui STDIN, argumen baris perintah atau argumen fungsi, dan Anda dapat mengembalikan hasilnya atau mencetaknya ke STDOUT. Anda dapat menggunakan format string atau daftar yang mudah digunakan untuk merepresentasikan input dan output (selama formatnya tidak ambigu dan inputnya tidak diproses sebelumnya). Juga, format kedua input harus konsisten (jadi Anda tidak dapat mengambil satu input sebagai string dan yang lainnya sebagai daftar, misalnya). Anda dapat mengambil daftar input dalam urutan apa pun, tetapi harap tentukan metode input yang tepat dalam jawaban Anda.

Satu batasan lagi: Anda tidak boleh menggunakan ekspresi reguler. Ini adalah tantangan manipulasi array, bukan tantangan manipulasi string.

Ini adalah kode golf, jadi jawaban tersingkat (dalam byte) menang.

Uji Kasus

Structure                             Values                 Result
[[[1,3],2],[1,4],12,[[0,0],[5,[7]]]]  [1,1,0,1,0,0,0,0,1,1]  [[[1,1],0],[1,0],0,[[0,0],[1,[1]]]]
[[[0,0],0],[0,0],0,[[0,0],[0,[0]]]]   [1,1,0,1,0,0,0,0,1,1]  [[[1,1],0],[1,0],0,[[0,0],[1,[1]]]]
[]                                    []                     []
[[]]                                  []                     [[]]
[0,1,2,3]                             [5,1,0,5]              [5,1,0,5]
[[[[[0]]]]]                           [123]                  [[[[[123]]]]]
[0,[1,[]],[[]],[2,3],[]]              [1,6,1,8]              [1,[6,[]],[[]],[1,8],[]]

Apakah diizinkan jika nilai dalam array Struktur bisa diubah?
ProgramFOX

@ProgramFOX ya. "Anda dapat memodifikasi array input dalam kode Anda."
Martin Ender

Ironisnya, salah satu kiriman di sini adalah di Mathematica.
Isiah Meadows

1
@impinball Itu milik saya, yang saya posting bersama dengan pertanyaan, untuk mencegah orang lain mencuri jawaban dari pertanyaan yang ditautkan (dan pada kenyataannya, itu hanya versi turun dari jawaban itu).
Martin Ender

@ MartinBüttner Oh. Bagus. Ini sebenarnya salah satu jawaban yang lebih pendek juga.
Isiah Meadows

Jawaban:


9

CJam, 18 16 13 byte

lA,sNerN%l~]z

Mengambil input melalui STDIN dalam format yang sama dengan jawaban CJam sebelumnya:

[0 [11 []] [[]] [2 3] []]
[1 6 1 8] 

dan output string hasil ke STDOUT

[1 [6 []] [[]] [1 8] []]

Saya hanya memperlakukan baris pertama sebagai string, mengubah semua karakter digit ke baris baru, membagi satu atau lebih kejadian baris baru, meletakkan baris kedua sebagai array di stack, membungkus array dan zip bersama dua array (baris). Pencetakan otomatis dan karena baris pertama diperlakukan sebagai string, ia tetap menggunakan tanda kurung.

Perluasan kode

lA,sNerN%l~]z
l                     "Read the first line of input. This is the nested array";
 A,s                  "Get array [0,1,2...9] and  convert it to string '012..9'";
    Ner               "Replace all occurrences of 0,1,2,..9 with new line";
       N%             "Split on one or more occurrences of new line";
         l~           "Read the second line as an array";
           ]          "Wrap both the splitted string and the second line array";
                      "in an array";
            z         "Transpose the array, there by placing the numbers from second";
                      "input array in the split holes of first input string";

Terima kasih kepada @ user23013 untuk menghemat 3 byte.

Cobalah online di sini


Dari OP, "Ini adalah tantangan manipulasi array, bukan tantangan manipulasi string."
atk

@ atk: Ini bisa diperdebatkan karena OP hanya secara eksplisit melarang ekspresi reguler.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

1
Pendek untuk /La-: %.
jimmy23013

@ user23013 Wow, tidak pernah repot untuk menyadari bahwa %itu untuk split juga, dan itu terbagi di beberapa kejadian juga!
Pengoptimal

@ ya. Karena hanya regex yang dilarang, saya menggunakan teknik ini.
Pengoptimal

25

JavaScript, ES6, 44 byte

f=(a,b,i=0)=>a.map(x=>x.map?f(x,b,i):b[i++])

Ini menciptakan fungsi fyang bisa disebut seperti

f([0,[1,[]],[[]],[2,3],[]],[1,6,1,8])

yaitu array bersarang dan array nilai sebagai argumen input. Output dari fungsi adalah array bersarang yang dikonversi.

Pertanyaan ini adalah pertanyaan yang sangat bagus untuk rekursi, itulah sebabnya jawabannya adalah fungsi rekursi yang rapi dan manis. Saya membuat fungsi fyang mengubah argumen pertama menggunakan mapmetode ini. Untuk setiap elemen, jika elemen adalah array, ia memanggil flagi, jika tidak, untuk bilangan bulat, ia mendapatkan item ke- i dan mengembalikannya, menambah nilai i. Nilai iditurunkan dalam setiap panggilan rekursif, sehingga menjaga urutan yang benar.

Deteksi array vs integer sekali lagi dilakukan menggunakan mapmetode ini. Untuk variabel array, mapadalah fungsi yang valid, sedangkan untuk variabel integer, tidak ada properti atau fungsi yang disebut mapdidefinisikan untuk variabel.

Ini berfungsi di peramban Firefox terbaru (karena ES6).


3
Saya tahu saya harus menghindari komentar seperti "+1" dan "terima kasih", tapi sialnya ini adalah salah satu fungsi ES6 yang manis! Saya dapat melihat baris kode ini selama berjam-jam :)
Yakub

Saya melihat ada 2 .mapkode. Apakah ada cara untuk memperpendeknya? Bagaimanapun, kode yang bagus!
Derek 朕 會 功夫

Whoa, kapan ES menambahkan sintaks lambda itu?
lembut

@fluffy dalam ES6;)
Pengoptimal

@Derek 朕 會 功夫 sayangnya tidak. mapterkait dengan konteks, sehingga peta pertama menjadi milik asementara peta berikutnya milik masing-masing xdalam iterasi. Tidak ada cara lain yang lebih singkat untuk merujuk map, juga tidak membedakan array dari bilangan bulat
Pengoptimal

18

JavaScript, ES6, 41 byte

Saya sangat terkesan dengan jawaban Pengoptimal , itu dilakukan dengan sangat cerdik dan saya belajar banyak. Namun saat melihatnya saya menemukan cara untuk memperpendeknya sedikit dan memperbaiki bug kecil:

f=(a,b)=>a.map(x=>x.map?f(x,b):b.shift())

Saya mengambil ivariabel dan menggantinya dengan shift(). Ini membuatnya sedikit lebih pendek dan memperbaiki masalah dengan fakta yang iditeruskan oleh nilai dan bukan oleh referensi, penyihir menyebabkan beberapa angka dari array terakhir untuk diulang dan beberapa di akhir tidak digunakan. Sekali lagi, jawaban Pengoptimal dipikirkan dengan sangat baik, lebih baik daripada yang bisa saya lakukan, saya hanya memperbaikinya sedikit.


2
Golf yang bagus! Sedikit sedih bahwa saya tidak menangkap itu: P
Optimizer

16

Dyalog APL, 14 karakter

Ini adalah no-brainer: (∊a)←b.

Biasanya, ∊aberarti adiratakan, tetapi ketika itu terjadi di sisi kiri penugasan, itu tepat apa masalah ini meminta. Untuk memenuhi persyaratan fungsi, diperlukan beberapa coretan tambahan: {a←⍺⋄(∊a)←⍵⋄a}(kurung kurawal untuk lambda; dan untuk argumen kiri dan kanan; untuk pemisah pernyataan).

Tes di tryapl.org. Perhatikan bahwa dalam APL vektor numerik kosong dilambangkan dengan ("zilde"). Vektor satu elemen dibangun dengan (,A)karena (A)akan berarti skalar. Dalam output, hal ini:

┌⊖┐
│0│
└~┘

mewakili vektor numerik kosong. Di 0tengah menunjukkan "elemen prototipe" yang bukan merupakan elemen dari array.


1
Apakah representasi grafis itu tidak membedakan (,1)dan (1)atau mengapa bit terakhir hanya disajikan sebagai [1|1]ganti [1|[1]]?
Martin Ender

Representasi grafis yang menggunakan tryapl (dikenal sebagai ]box on) tidak membedakan di antara mereka. Ada fungsi lain di Dyalog ( displaydari dfns.dws) yang membuat perbedaan, tetapi sayangnya tryapl membatasi pemuatan ruang kerja tambahan (yaitu perpustakaan). :(
ngn

1
Untuk melihat hasil dalam bentuk persegi tanda kurung, coba ini: ∊{0=⍴⍴⍵:⍕⍵ ⋄ '['(∇¨⍵)']'}a. Atau ini: ∊{0=⍴⍴⍵:⍕⍵ ⋄ '['(1↓,'|',[1.5]∇¨⍵)']'}ajika Anda bersikeras pada pemisah |,.
ngn

Oh, Anda juga bisa menggunakannya ]display adi tryapl. Ini memberikan informasi lengkap tentang struktur. Maaf, saya tidak menyadarinya pada awalnya.
ngn

Titik adil. Saya mengubahnya menjadi fungsi dengan biaya 2 byte tambahan.
ngn

10

Python, 51

f=lambda a,b:[b.pop(0)if x<[]else f(x,b)for x in a]

Contoh:

>>> f([0,[1,[]],[[]],[2,3],[]], [1,6,1,8])
[1, [6, []], [[]], [1, 8], []]

10

Python 2, 50

f=lambda s,v:v.pop(0)if s<[]else[f(x,v)for x in s]

Ini adalah masalah yang sangat indah. Ketika saya terus mengerjakannya, saya terus menyadari bahwa bit kode saya tidak perlu, dan logikanya runtuh menjadi ekspresi sederhana. Sebagian besar golf dalam menemukan algoritma yang tepat.

sadalah struktur dan vdaftar datar daftar. Idenya adalah untuk memeriksa apakah sbilangan bulat dengan s<[](Python 2 memperlakukan angka lebih kecil dari daftar). Jika ya, cukup ambil dan kembalikan elemen pertama v, hapus dari v. Jika tidak, kembalilah ke sublists dari s.

Ini pop adalah bagian dari imperative magic dalam kode gaya yang sangat fungsional. Karena semua vmenunjuk ke instance yang sama, memunculkan elemen dari satu menghapusnya dari vdalam seluruh pohon eksekusi, sehingga setiap angka vhanya digunakan satu kali. Pemahaman daftar [f(x,v)for x in s]membuat pohon panggilan yang diperluas kedalaman-pertama dan kiri-ke-kanan, menyebabkan elemen-elemen yang vditempatkan dalam urutan yang benar.

Saya menulis ini secara independen dari jawaban grc , tetapi ternyata sama untuk memindahkan satu [(dan nama variabel). Langkah ini menyimpan char karena jarak. Braket bergerak berarti menangani kasus simpul segera dalam fungsi, bukan sebagai bagian dari pemahaman daftar, yang belum saya pertimbangkan.

Kita bisa menyimpan char untuk 49 jika kita meregangkan persyaratan input untuk mengambil nilai dari STDIN dan struktur sebagai argumen fungsi. Ini memungkinkan kita menggunakan map.

v=input()
g=lambda s:v.pop(0)if s<[]else map(g,s)

9

Ruby, 39

f=->a,b{a.map{|d|f[d,b]}rescue b.shift}

Perulangan hingga elemen dalam daftar adalah bilangan bulat.
Karena memanggil Integer.map memberikan pengecualian,
ia pergi ke bagian penyelamatan, yang "muncul / geser" elemen 1 dari daftar ke-2.

Regex soln ... sedikit lebih lama:

f=->a,b{eval a.to_s.split(/\d+/).zip(b)*''}

Cobalah dengan beberapa test case


Hanya untuk referensi, solusi regex tidak diperbolehkan. ;)
Martin Ender

5

CJam, 43 37 35 33 byte

Yang ini adalah konversi langsung dari jawaban JS saya . Agak panjang, sebagian besar diambil oleh deteksi tipe.

q~:B;{{_`La`&{F}{;BW):W=}?}%}:F~`

Mengambil dua array input pada dua baris seperti STDIN

[[[1 3] 2] [1 4] 12 [] [[0 0] [5 [7]]]]
[1 1 0 1 0 0 0 0 1 1]

dan output ke STDOUT suka

[[[1 1] 0] [1 0] 0 "" [[0 0] [1 [1]]]]

Cobalah online di sini


5

Haskell, 113 104 bytes (86 + 18 dari deklarasi tipe data)

data N=I Int|L[N]
L[]!v=(L[],v)
L(a:b)!v|(c,w)<-a!v,(L d,u)<-L b!w=(L$c:d,u)
_!(n:m)=(I n,m)
s#v=fst$s!v

Haskell tidak memiliki datatype array bawaan, jadi saya harus roll sendiri. Untuk alasan ini, program hanya berisi pencocokan pola dan rekursi struktural eksplisit. Kasing tes terakhir berbunyi

L[I 0,L[I 1,L[]],L[L[]],L[I 2,I 3],L[]]#[1,6,1,8]

dan mengevaluasi ke

L[I 1,L[I 6,L[]],L[L[]],L[I 1,I 8],L[]]

4

Mathematica, 41 byte

Function[,m[[i++]],Listable][i=1;m=#2;#]&

Ini adalah fungsi tanpa nama yang mengambil struktur sebagai argumen pertama dan daftar nilai sebagai argumen kedua (dan mengembalikan daftar).

Ini adalah versi golf dari jawaban yang diterima pada pertanyaan yang menginspirasi tantangan ini. Saya memposting ini sendiri, dan tidak akan menerima jawaban ini (haruskah itu benar-benar tetap terpendek, yang saya ragu). Ini untuk mencegah orang lain dari memenangkan tantangan dengan menyalin jawaban pada dasarnya.

Bagaimana itu bekerja:

  • Kami mendefinisikan Listablefungsi murni. Fungsi yang dapat didaftarkan secara otomatis diterapkan pada elemen argumen daftar (secara rekursif) alih-alih daftar itu sendiri, sehingga memanggil fdaftar terstruktur pada dasarnya akan mengembalikan daftar struktur yang sama dengan setiap bilangan bulat idiganti oleh f[i].
  • Kami menyimpan daftar nilai di global mdan penghitung dii .
  • Setiap kali kami menelepon f(terlepas dari argumennya), kami mengembalikan elemen berikutnya m.

4

Rebol - 87 66 60

f: func[a[block!]b][map-each n a[any[attempt[f n b]take b]]]

Tidak Disatukan:

f: func [a [block!] b] [
    map-each n a [
        any [
            attempt [f n b]  
            take b
        ]
    ]
]

Contoh:

>> f [0 [1 []] [[]] [2 3] []]   [1 6 1 8]           
== [1 [6 []] [[]] [1 8] []]

4

C #, 225 + 13 = 239 185 + 35 = 220 172 + 35 = 207 byte

Membutuhkan ini:

using System;using o=System.Object;

Terima object[]s sebagai argumen.

o[]u(o[]a,o[]b){var c=a;int i=0;Action<o[],o[]>d=null;d=(e, f)=>{for(int j=0;j<e.Length;j++){if(e[j]is int){f[j]=b[i];i++;}else{d((o[])e[j],(o[])f[j]);}}};d(a,c);return c;}

Kode tidak dikunci:

object[] Unflatten(object[] structure, object[] values)
{
    var c = structure;
    int i = 0;
    Action<object[], object[]> recursiveFunc = null;
    recursiveFunc = (e, f) =>
    {
        for (int j = 0; j < e.Length; j++)
        {
            if (e[j] is int)
            {
                f[j] = values[i]; i++;
            }
            else
            {
                recursiveFunc((object[])e[j], (object[])f[j]);
            }
        }
    };
    recursiveFunc(structure, c);
    return c;
}

2
Anda dapat mempersingkat lebih sedikit dengan menggunakan using o=System.Objectdan mengganti semua contoh objectdengan sederhana o. msdn.microsoft.com/en-us/library/sf0df423.aspx
Kroltan

1
@ Kroltan Tip luar biasa, terima kasih!
ProgramFOX

Clonedangkal. Jika modifikasi input diperbolehkan, Anda tidak perlu mengkloning sama sekali. Jika tidak diizinkan, Anda perlu kloning yang tepat.
CodesInChaos

@CodesInChaos saya mengerti. Saat memodifikasi array input diperbolehkan, saya menghapus klon. Terima kasih!
ProgramFOX

3

Python 2, 64 byte

def g(N,L):f=lambda N:L.pop(0)if`N`<":"else map(f,N);return f(N)

Saya mendengar Anda suka daftar di daftar jadi saya menempatkan fungsi dalam fungsi.

Sunting: Melihat jawaban grc sekarang saya menyadari bahwa itu sama sekali tidak perlu. Baiklah...


3

SWI-Prolog 82

f([],A,[],A):-!.
f([H|T],A,[J|U],B):-(is_list(H),!,f(H,A,J,C);A=[J|C]),f(T,C,U,B).

Contoh dijalankan:

?- f([[[1,3],2],[1,4],12,[[0,[],0],[5,[7]]]],[1,1,0,1,0,0,0,0,1,1],R,[]).
R = [[[1,1],0],[1,0],0,[[0,[],0],[1,[1]]]].

Yang terakhir []dalam kueri adalah untuk memeriksa jumlah elemen yang tidak cocok, yang tampaknya tidak diperlukan dalam pertanyaan ini.


Apa yang membuat pemotongan (dan, lebih lanjut, mahal is_list) diperlukan?
String Tidak Terkait

1
@UnrelatedString: Jangan ragu untuk mengedit jawaban secara langsung jika Anda merasa tidak perlu mendapatkan jawaban yang benar. Prolog saya buruk saat itu (saya menggunakan perpustakaan dan memotong secara luas) dan bahkan lebih berkarat akhir-akhir ini.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳

2

Erlang, 116 93 Bytes

f(R,F)->put(n,F),[g(X)||X<-R].
g([H|T])->[g(H)|g(T)];g([])->[];g(E)->[H|T]=get(n),put(n,T),H.

Menggunakan dua fungsi tidak murni fdan g. fmemanipulasi kamus proses dengan mengatur nke daftar datar dan memetakan setiap elemen dari daftar bersarang g(X). gkemudian atur nke ekor daftar datar setiap kali menemukan nilai non-daftar dan mengembalikan kepala daftar datar.


1

Perl 5, 49 byte

Argumen pertama adalah struktur template, kedua adalah nilai-nilai.

sub u{($t,$l)=@_;ref$t?[map{u$_,$l}@$t]:shift@$l}

Program Tes

use Test::More;
use Test::Deep;

sub u{($t,$l)=@_;ref$t?[map{u$_,$l}@$t]:shift@$l}

cmp_deeply u([[[1,3],2],[1,4],12,[[0,0],[5,[7]]]],[1,1,0,1,0,0,0,0,1,1]),[[[1,1],0],[1,0],0,[[0,0],[1,[1]]]];
cmp_deeply u([[[0,0],0],[0,0],0,[[0,0],[0,[0]]]],[1,1,0,1,0,0,0,0,1,1]),[[[1,1],0],[1,0],0,[[0,0],[1,[1]]]];
cmp_deeply u([], []), [];
cmp_deeply u([[]], []), [[]];
cmp_deeply u([0,1,2,3], [5,1,0,5]), [5,1,0,5];
cmp_deeply u([[[[[0]]]]], [123]), [[[[[123]]]]];
cmp_deeply u([0,[1,[]],[[]],[2,3],[]], [1,6,1,8]), [1,[6,[]],[[]],[1,8],[]];
done_testing;

1

Powershell: 115

array input adalah $ i, pemetaan adalah $ m, output adalah $ o

$h={if($_.GetType().IsArray){if($_.c -eq 0){,@()}else{,@($_|%{.$h})}}else{$m[$c++]}};$i|%{$o=@();$c=0}{$o+=,(.$h)}

$ h adalah sebuah string yang berisi fungsi rekursif, dan Anda dapat mengeksekusi kode yang terkandung dalam sebuah string dengan. $ h ... Dan itu akan menjadi 30 byte lebih pendek jika PowerShell tidak bersikeras meratakan array nilai tunggal untuk skalar, dan sebuah array dengan nilai nol tunggal ke nol

dan penampil struktur array yang berguna untuk memverifikasi hasil

$j={if($_.GetType().IsArray){write-host '(' -n;($_|%{.$j});write-host ')' -n}else{write-host "$_" -n}};write-host '(' -n;$o|%{(.$j)}; write-host ')' -n;

sunting: 149

simpan sebagai unflatten.ps1:

$m=[array]$args[1];$h={if($_.GetType().IsArray){if($_.c -eq 0){,@()}else{,@($_|%{.$h})}}else{$m[$c++]}};$args[0]|%{$o=@();$c=0}{$o+=,(.$h)};echo $o;

edit: 136, pembuatan array inline output dan write-output

$m=[array]$args[1];$h={if($_.GetType().IsArray){if($_.c -eq 0){,@()}else{,@($_|%{.$h})}}else{$m[$c++]}};echo(,@($args[0]|%{$c=0}{.$h}))

panggil dengan. \ unflatten.ps1 [array input] [array pemetaan]

output ditulis ke pipeline- jadi jalankan ini terlebih dahulu:

Function View-Array{
Param([Parameter(ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)]
      [array]$o)

    PROCESS{
    $j={if($_.GetType().IsArray){write-host '(' -n;($_|%{.$j});write-host ')' -n}else{write-host "$_" -n}};
    write-host '(' -n;$o|%{(.$j)}; write-host ')' -n;
    }
}

dan jalankan dengan

.\unflatten.ps1 [input array] [mapping array] | View-Array

1

C #, (40 + 123) = 163 byte OR (67 + 81) = 148 byte

C # menderita pengetikan statis dan ruang nama yang panjang di sini.

Metode array

Menggunakan pernyataan:

using o=System.Object;using System.Linq;

Kode:

o[] u(o[] x,o[] y){int i=0;Func<o[],o[],o[]> f=null;f=(a,b)=>a.Select(e=>e is int?b[i++]:f((o[])e,b)).ToArray();return f(x,y);}

Metode tumpukan (menggunakan struktur tumpukan bukannya array)

Menggunakan pernyataan:

using s=System.Collections.Generic.Stack<object>;using System.Linq;

Kode:

System.Func<s,s,s>f=null;f=(a,b)=>new s(a.Select(e=>e is int?b.Pop():f((s)e,b)));

Upaya pertama, kode golf pertama di sini.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.