Lời khuyên cho việc chơi golf trong VBA


16

Tương tự như thế này , đây và câu hỏi này ...

Bạn có lời khuyên chung nào cho việc chơi golf VBA? Tôi đang tìm kiếm những ý tưởng có thể được áp dụng cho các vấn đề về golf nói chung ít nhất là cụ thể đối với VBA(ví dụ: "xóa bình luận" không phải là một câu trả lời). Xin vui lòng gửi một lời khuyên cho mỗi câu trả lời.

Trong khi tôi đã làm việc với các ngôn ngữ khác, tôi mạnh nhất VBAvà tôi không thấy nhiều người chơi golf sử dụng VBAtrên trang web này.


Chuyển đổi sang Cộng đồng Wiki theo chính sách.
dmckee

Xin lỗi đó không phải là tự động về phía tôi!
Gaffi

Không vấn đề gì. Trên thực tế, họ đã lấy sức mạnh để đặt câu hỏi CW khỏi người dùng (tôi vẫn có thể trả lời, tôi nghĩ vậy). Bạn có thể gắn cờ cho sự chú ý của người điều hành, nhưng càng ít hoạt động như CodeGolf thì điều đó hầu như không cần thiết.
dmckee

2
VBA là một ngôn ngữ tương đối dài dòng với một vài phím tắt cú pháp. Nếu bạn đang đạt điểm cao nhất, VBA có thể không phải là một lựa chọn tốt. Nếu bạn đang tìm cách trau dồi kỹ năng của mình, hãy tiếp thêm sức mạnh cho bạn.
Ông Llama

2
@GigaWatt Hoàn thiện kỹ năng của tôi. Thực tế kể từ khi chơi xung quanh với những thử thách khác nhau, tôi đã chọn ra một vài thủ thuật mới để làm việc với VBA! Tôi không mong đợi để giành chiến thắng bất kỳ thực mã golf thách thức với VBA, nhưng đó là thực hành tốt. :-)
Gaffi

Câu trả lời:


8

Khai thác ByRefmặc định khi gọi subs

Đôi khi có thể sử dụng một Subcuộc gọi thay cho một Functionđể lưu thêm một vài ký tự ...

Cái này ( 87 ký tự)

Sub a()
b = 0
Do Until b = 5
b = c(b)
Loop
End Sub
Function c(d)
c = d + 1
End Function

có thể được làm lại để ( 73 ký tự):

Sub a()
b = 0
Do Until b = 5
c b
Loop
End Sub
Sub c(d)
d = d + 1
End Sub

Lưu ý rằng điều này sẽ KHÔNG lặp lại mãi mãi, mặc dù có vẻ như bạn sẽ không bao giờ gán lại bgiá trị của nó.

Ở trên không sử dụng Functioncuộc gọi, mà thay vào đó khai thác chức năng ByRef("Theo tham chiếu") của Subcuộc gọi. Điều này có nghĩa là đối số được truyền là cùng một biến như trong hàm gọi (trái ngược với ByValthông qua "Theo giá trị", là một bản sao). Mọi sửa đổi đối với biến đã truyền sẽ chuyển trở lại chức năng gọi.

Theo mặc định, lấy tất cả các đối số là ByRef, vì vậy không cần sử dụng hết các ký tự để xác định điều này.

Ví dụ trên có thể không dịch hoàn hảo cho bạn, tùy thuộc vào giá trị trả về của hàm. (tức là trả về một kiểu dữ liệu khác với dữ liệu được truyền), nhưng điều này cũng cho phép khả năng nhận được giá trị trả về trong khi vẫn sửa đổi biến ban đầu của bạn.

Ví dụ:

Sub a()
b = 0
Debug.Print c(b) ' This will print 0, and b will equal 1.'
End Sub
Function c(d)
c = d
d = d + 1
End Function

7

Viết và chạy mã VBA trong Cửa sổ ngay lập tức

Cửa sổ ngay lập tức đánh giá bất kỳ câu lệnh thực thi VBA hợp lệ nào. Chỉ cần nhập một câu lệnh trong Cửa sổ ngay lập tức như trong trình soạn thảo mã. Nó nhanh chóng thực thi mã VBA và nó có thể lưu nhiều ký tự bổ sung vì:

  1. Đặt dấu hỏi (?) Ở đầu câu lệnh sẽ cho Cửa sổ ngay lập tức hiển thị kết quả mã của bạn.

nhập mô tả hình ảnh ở đây

  1. Bạn không cần sử dụng Sub và End Sub trong mã của mình.

nhập mô tả hình ảnh ở đây


Dưới đây là ví dụ về mã VBA trong Cửa sổ ngay lập tức để trả lời bài đăng của PPCG bằng thẻ : Chữ A không có A

?Chr(88-23);

trả lời bởi Joffan .


Hình ảnh tín dụng: Cơ sở Excel


1
Điều đó sử dụng một môi trường REPL mặc dù? Điều đó có nghĩa là nó chỉ là một đoạn trích, không phải là một chương trình hay chức năng
caird coinheringaahing

Tôi nghĩ một ví dụ khác để thêm có thể là bạn cũng có thể tạo các biến nội tuyến; ví dụ a="value":?ađầu tiên đặt giá trị của a, sau đó in nó.
Greedo

6

Kiểm tra có điều kiện trước khi vòng

Một số kiểm tra có điều kiện là dư thừa khi được sử dụng kết hợp với các vòng lặp. Ví dụ:For vòng lặp sẽ không xử lý nếu điều kiện bắt đầu nằm ngoài phạm vi của điều kiện đang chạy.

Nói cách khác, điều này ( 49 ký tự):

If B > 0 Then
For C = A To A + B
'...
Next
End If

Có thể biến thành thế này ( 24 ký tự):

For C = A To A + B ' if B is 0 or less, then the code continues past Next, unabated.
'...
Next

Nhận xét ở phía dưới là không chính xác. Nếu B = 0 thì vòng lặp sẽ được nhập. pastebin.com/97MBB7hq
QHarr

5

Sự định nghĩa biến

Trong hầu hết các trường hợp trong VBA, bạn có thể bỏ qua Option Explicit(thường bị bỏ qua theo mặc định) và bỏ quaDim nhiều biến của bạn.

Khi làm như vậy, điều này ( 96 Chars):

Option Explicit

Sub Test()
Dim S As String
Dim S2 As String
S = "Test"
S2 = S
MsgBox S2
End Sub

Trở thành này ( 46 ký tự):

Sub Test()
S = "Test"
S2 = S
MsgBox S2
End Sub

Nếu bạn cần sử dụng một số đối tượng nhất định (ví dụ: mảng), bạn vẫn có thể cần đến Dimbiến đó.


Đợi một chút, bạn chấp nhận câu trả lời của riêng bạn cho câu hỏi của bạn. Không ngầu, anh bạn.
Taylor Scott

1
@TaylorScott Đó là một bài viết wiki - không có điểm nào, và trong trường hợp này, không có một câu trả lời hay nhất. :) Tôi chấp nhận câu trả lời ~ 5 năm trước để tránh có cờ trong danh sách câu hỏi của tôi.
Gaffi

1
@TaylorScott cũng nếu bạn chấp nhận câu trả lời của riêng mình, bạn không nhận được +15 hoặc +2 (vì đã chấp nhận)
caird coinheringaahing

5

Evaluate()[]

Như đã được chỉ ra trước đây, các cuộc gọi phạm vi được mã hóa cứng có thể được giảm bằng cách sử dụng [A1]ký hiệu dấu ngoặc vuông . Tuy nhiên, nó có nhiều công dụng hơn thế.

Theo tài liệu MSDN , Application.Evaluate()phương thức này lấy một đối số duy nhất, là một Name, như được định nghĩa bởi quy ước đặt tên của Microsoft Excel.

NB [string]là viết tắt cho Evaluate("string")(lưu ý"" dấu hiệu biểu thị kiểu dữ liệu chuỗi), mặc dù có một vài khác biệt quan trọng được đề cập ở phần cuối.

Vì vậy, tất cả những gì có nghĩa là về mặt sử dụng?

Về cơ bản, [some cell formula here]đại diện cho một ô trong bảng tính excel và bạn có thể đặt khá nhiều thứ vào đó và lấy bất cứ thứ gì có thể với một ô bình thường

Những gì đi vào

Tóm lại, với các ví dụ

  • Tài liệu tham khảo kiểu A1. Tất cả các tài liệu tham khảo được coi là tài liệu tham khảo tuyệt đối.
    • [B7] trả về một tham chiếu đến ô đó
    • Vì tài liệu tham khảo là tuyệt đối, ?[B7].Addresstrả về"B$7$"
  • Phạm vi Bạn có thể sử dụng phạm vi, giao điểm và toán tử hợp (tương ứng dấu hai chấm, dấu cách và dấu phẩy) với các tham chiếu.
    • [A1:B5]trả về một tham chiếu phạm vi cho A1:B5(Phạm vi)
    • [A1:B5 A3:D7]trả về một tham chiếu phạm vi đến A3:B5(Intersect)
    • [A1:B5,C1:D5]trả về một phạm vi tham chiếu đến A1:D5(về mặt kỹ thuật A1:B5,C1:D5) (Liên minh)
  • Tên được xác định
    • Người dùng xác định, chẳng hạn như [myRange]tham khảo A1: G5
    • Tự động, như tên bảng [Table1](có thể ít hữu ích hơn cho codegolf)
    • Bất cứ điều gì khác bạn có thể tìm thấy trong menu [Formulas]>[Name Manager]
  • Công thức
    • Rất hữu ích ; bất kỳ công thức nào có thể đi vào trong một tế bào đều có thể đi vào Evaluate()hoặc[]
    • [SUM(1,A1:B5,myRange)]trả về tổng số học của các giá trị trong phạm vi myRange ở đây đề cập đến tên bảng tính không phải là biến VBA
    • [IF(A1=1,"true","false")](1 byte ngắn hơn tương đương vba Iif([A1]=1,"true","false"))
    • Công thức mảng [CONCAT(IF(LEN(A1:B7)>2,A1:B7&" ",""))]- nối tất cả các chuỗi trong phạm vi có độ dài lớn hơn 2 với khoảng trắng
  • Tài liệu tham khảo bên ngoài
    • Bạn có thể sử dụng !toán tử để chỉ một ô hoặc tên được xác định trong sổ làm việc khác
    • [[BOOK1]Sheet1!A1]trả về một tham chiếu phạm vi cho A1 trong BOOK1 hoặc ['[MY WORKBOOK.XLSM]Sheet1!'A1]tương tự trongMY WORKBOOK
    • Lưu ý các 'sổ làm việc có khoảng trắng trong tên của chúng và thiếu phần mở rộng cho sổ làm việc có tên mặc định ( BOOK+n)
  • Đối tượng biểu đồ (Xem bài viết MSDN)

NB Tôi đã hỏi một câu hỏi về SO cho thông tin này, vì vậy hãy xem ở đó để được giải thích tốt hơn

Những gì đi ra

Tương tự như những gì đi vào, những gì đi ra bao gồm bất cứ thứ gì mà một ô bảng tính có thể trả về. Đây là các loại dữ liệu Excel tiêu chuẩn (và tương đương VBA của chúng):

  • Hợp lý ( Boolean)
  • Văn bản ( String)
  • Số ( Double)
  • Error ( Variant/Error) (đây là những lỗi không vi phạm, nghĩa là chúng không dừng thực thi mã, ?[1/0]thực thi tốt)

Nhưng có một vài điều có thể được trả về mà các ô không thể:

  • Phạm vi tham chiếu ( Range) (xem các phần trên)
  • Mảng ( Variant())

Mảng

Như đã được chỉ ra, []có thể được sử dụng để đánh giá các công thức mảng trả về một trong các kiểu dữ liệu Excel tiêu chuẩn; ví dụ như [CONCAT(IF(LEN(A1:B7)>2,A1:B7&" ",""))]trả về Text. Tuy nhiên Evaluatecũng có thể trả về mảng Variantloại. Đây có thể là

Mã hóa cứng:

[{1,2;3,4}]- xuất ra một mảng 2D: ,là dấu phân cách cột, ;phân tách các hàng. Có thể xuất ra mảng 1D

Công thức mảng:

[ROW(A1:A5)]- xuất ra một mảng 2D{1,2,3,4,5} , tức là (2.1) là mục thứ hai (một số chức năng xuất ra các mảng 1D)

  • Vì lý do nào, một số hàm không trả về mảng theo mặc định

[LEN(A1:A5)] chỉ xuất ra Độ dài của văn bản trong ô thứ nhất của một phạm vi

  • Tuy nhiên, những điều này có thể bị ép buộc để đưa ra mảng

Split([CONCAT(" "&LEN(A1:A5))]) đưa ra mảng 1D từ 0 đến 5, trong đó mục đầu tiên trống

[INDEX(LEN(A1:A5),)]là một cách giải quyết khác, về cơ bản, bạn phải sử dụng một hàm xử lý mảng để có được hành vi trả về mảng mong muốn, tương tự như việc thêm vào một cách vô nghĩa RAND()để làm cho các công thức bảng tính của bạn biến động.

  • Tôi đã hỏi một câu hỏi về SO để cố gắng có được thông tin tốt hơn về vấn đề này, vui lòng chỉnh sửa / nhận xét với các tùy chọn tốt hơn nếu bạn tìm thấy chúng

Evaluate() đấu với []

Có một vài sự khác biệt giữa Evaluate()[]để nhận thức về

  1. Chuỗi vs mã hóa cứng
    • Có lẽ sự khác biệt quan trọng nhất, Đánh giá có một đầu vào chuỗi trong đó [] yêu cầu đầu vào được mã hóa cứng
    • Điều này có nghĩa mã của bạn có thể xây dựng các chuỗi với các biến số ví dụ Evaluate("SUM(B1,1,"&v &",2)")sẽ tổng hợp [B1], 1, 2và biếnv
  2. Mảng

    Khi trả về một mảng, chỉ có thể sử dụng Đánh giá với chỉ mục mảng, vì vậy

    v=Evaluate("ROW(A1:A5)")(1,1) ''#29 bytes

    tương đương với

    i=[ROW(A1:A5)]:v=i(1,1) ''#23 bytes

Xin lỗi cho bài viết của voi ma mút, nếu bất cứ ai cảm thấy họ có thể làm cho các khu vực ngắn gọn hơn / nếu bạn có mẹo để thêm, thì hãy thoải mái. Ngoài ra, mặc dù tôi đã nghiên cứu một số điều này, một phần công bằng đã được tìm thấy thông qua thử nghiệm trong VBA, vì vậy nếu bạn phát hiện ra những sai lầm về cảm giác thiếu thứ gì đó, đừng ngần ngại bình luận / chỉnh sửa cho phù hợp!
Greedo

1
Trên thực tế, phần cuối cùng của mảng là một chút tắt - nó có thể được sử dụng trong cửa sổ ngay lập tứci=[ROW(A1:A5)]:v=i(2,1):?v
Taylor Scott

@TaylorScott Vì vậy, bạn có thể, tôi hoàn toàn không biết rằng bạn có thể đặt / giữ các giá trị trong các biến chưa được xác định, nhưng tôi cho rằng đó là vì tôi vẫn đang nắm bắt được cửa sổ 1 cửa sổ ngay lập tức. Đã cập nhật tương ứng
Greedo

Cái này có hoạt động với Match không? Tôi đã thử, nhưng nó luôn trả về lỗi. Tôi đã vượt qua một mảng thay vì một phạm vi, mặc dù.
seadoggie01

5

NextBáo cáo kết hợp

Next:Next:Next

Có thể ngưng tụ xuống

Next k,j,i

nơi lặp cho Forvòng là i, jk- theo thứ tự đó.

Ví dụ: bên dưới (69 byte)

For i=0To[A1]
For j=0To[B1]
For k=0To[C1]
Debug.?i;j;k
Next
Next
Next

Có thể được ngưng tụ xuống 65 byte

For i=0To[A1]
For j=0To[B1]
For k=0To[C1]
Debug.?i;j;k
Next k,j,i

Và theo như cách mà điều này tác động đến định dạng và thụt lề, tôi nghĩ rằng cách tiếp cận tốt nhất để xử lý việc này là trái với câu lệnh tiếp theo với phần lớn nhất cho câu lệnh. Ví dụ.

For i=0To[A1]
    For j=0To[B1]
        For k=0To[C1]
            Debug.?i;j;k
Next k,j,i

4

IfBáo cáo giảm

Khi gán một biến bằng cách sử dụng một điều kiện If ... Then ... Else kiểm tra có , bạn có thể giảm số lượng mã được sử dụng bằng cách loại bỏ End Ifbằng cách đặt toàn bộ kiểm tra trên một dòng.

Ví dụ: cái này ( 37 ký tự):

If a < b Then
c = b
Else
c = a
End If

Có thể giảm xuống này ( 30 ký tự)

If a < b Then c = b Else c = a

Nếu bạn có nhiều hơn một điều kiện lồng nhau, bạn cũng có thể giảm thiểu chúng theo cách này:

If a Then If b Then If c Then Z:If d Then Y:If e Then X Else W Else V:If f Then U 'Look ma! No "End If"!

Lưu ý : cho phép bạn thêm nhiều dòng / lệnh trong một Ifkhối.

Trong các trường hợp đơn giản như thế này, bạn cũng có thể loại bỏ Elsebằng cách đặt biến trước khi Ifkiểm tra ( 25 ký tự):

c = a
If a < b Then c = b

Thậm chí tốt hơn, ở trên có thể được giảm hơn nữa bằng cách sử dụng IIf()chức năng ( 20 ký tự):

c = IIf(a < b, b, a)

3

Giảm Range("A1") và thích các cuộc gọi

Range("A1").Value(17 byte) và đơn giản hơn Range("A1")(11 byte) có thể được giảm xuống còn [A1](4 byte)


2
Hay nói chung, việc sử dụng []để thay thế Evaluate()trong VBA linh hoạt hơn rất nhiều so với chỉ các cuộc gọi phạm vi. Ví dụ: bạn có thể sử dụng nó để thay thế WorksheetFunction.FuncName(args)bằng chỉ [FuncName(args)], chẳng hạn như ?[SUMPRODUCT({1,3,5},{2,7,-1})]hoặc rất hữu ích [MIN(1,2,3)]/[MAX(1,2,3)]
Greedo

Greedo, đó là một chức năng cực kỳ quan trọng mà tôi không biết về bạn nên thêm nó vào như câu trả lời của riêng nó.
Taylor Scott

1
Chắc chắn, tôi sẽ viết một cái gì đó lên trong thời gian ngắn. Có Evaluate("str")hoặc tốc ký [str]rất mạnh trong VBA, tôi mới chỉ phát hiện ra gần đây và tôi đoán nó cũng hữu ích cho việc chơi golf!
Greedo

Rất hữu ích, đủ để tôi quay lại câu trả lời của mình để xem liệu tôi có thể bỏ qua số tiền lẻ của mình không vì nó
Taylor Scott

3

Xóa không gian

VBA sẽ tự động định dạng để thêm nhiều khoảng cách mà nó không thực sự cần. Có một câu trả lời về meta rất có ý nghĩa với tôi tại sao chúng ta có thể giảm giá byte được thêm bằng cách tự động định dạng. Tuy nhiên, điều quan trọng là luôn xác minh rằng bạn chưa xóa quá nhiều. Ví dụ, đây là câu trả lời của tôi đã giảm gần 22% chỉ bằng cách xóa khoảng trắng:

Phiên bản gốc: (188 byte)

Sub g(n)
For i = 0 To 1
For x = -3 To 3 Step 0.05
y = n ^ x * Cos(Atn(1) * 4 * x)
If y < m Then m = y
If i = 1 Then Cells(500 * (1 - y / m) + 1, (x + 3) * 100 + 1) = "#"
Next
Next
End Sub

Phiên bản rút gọn: (146 byte)

Sub g(n)
For i=0To 1
For x=-3To 3Step 0.05
y=n^x*Cos(Atn(1)*4*x)
If y<m Then m=y
If i=1Then Cells(500*(1-y/m)+1,(x+3)*100+1)="#"
Next
Next
End Sub

Nếu bạn sao chép / dán phiên bản rút gọn vào VBA, nó sẽ tự động mở rộng thành bản gốc. Bạn có thể chơi xung quanh với nơi hợp lệ để xóa khoảng trắng. Những cái mà tôi đã tìm thấy cho đến nay dường như đều tuân theo mô hình mà VBA dự kiến ​​sẽ xuất hiện một lệnh nhất định bởi vì, nếu không có nó, đó không phải là mã hợp lệ. Đây là một số mà tôi đã tìm thấy cho đến nay:

  • Trước và sau bất kỳ hoạt động toán học nào: +-=/*^ vv
  • Trước ToSteptrong một Fortuyên bố:For x=-3To 3Step 0.05
  • Trên thực tế, trước khi bất kỳ từ dành riêng ( To, &, Then, vv) nếu nó được đi trước bởi một chữ như một con số, ), ?,% vv
  • Sau &khi kết hợp chuỗi:Cells(1,1)=Int(t)&Format(t,":hh:mm:ss")
  • Sau khi gọi hàm: If s=StrReverse(s)Then
  • Trong các cuộc gọi chức năng: Replace(Space(28)," ",0)

Quy tắc về các ký tự không phải không gian được thêm vào thông qua định dạng tự động. Một cái gì đó như ?vs Printtôi thấy là chấp nhận được, nhưng đưa vào khai báo kiểu như &ngay sau một biến, ví dụ như Debug.?a&"z"đưa ra Debug.Print a&; "z". Ký ;tự được thêm vào là điều cần thiết để mã chạy - điều này có còn được phép không?
Greedo

2
@Greedo Tôi thấy nó theo cách mà câu trả lời meta đã mô tả nó: Nếu bạn có thể sao chép / dán nó vào trình chỉnh sửa và mã sẽ chạy, nó ổn. IE, nếu VBA tự động thêm ký tự trở lại khi bạn dán mã, thì nó ổn.
Kỹ sư Toast

1
@EngineerToast bạn thực sự có thể bỏ thêm một byte trong ví dụ của mình: If i=1 Then có thể trở thành If i=1Then bởi vì, theo quy tắc chung, một từ dành riêng theo sau một nghĩa đen, có thể là một số ),,? , %, .. vv, không cần phải được ngăn cách bởi một không gian
Taylor Scott

1
@EngineerToast, bạn cũng có thể thả một byte từ dấu đầu dòng thứ ba của mình khi bạn có thể thả khoảng trắng giữa một )(hoặc bất kỳ chữ không số nào khác) và&
Taylor Scott

3

Chuẩn bị cho nghệ thuật Pixel

Pixel art là một trong những lĩnh vực mạnh nhất của Excel, vì không cần phải xây dựng một khung vẽ, vì nó đã có sẵn cho bạn - tất cả những gì bạn cần làm là thực hiện một số điều chỉnh nhỏ

1) Làm cho các ô vuông

Cells.RowHeight=48

hoặc, Ngoài ra, để có thêm 1 byte, nhưng dễ dàng hơn để làm việc với

Cells.ColumnWidth=2

2) Tô màu phạm vi cần thiết

Bất kỳ Rangeđối tượng có thể được tô màu bằng cách gọi

`MyRangeObj`.Interior.Color=`ColorValue`

Ghi chú: điều này có thể và nên được kết hợp với các thủ thuật khác được ghi chú trên wiki này, chẳng hạn như phạm vi tham chiếu bằng cách sử dụng []ký hiệu (ví dụ [A1:R5,D6]) qua việc sử dụng a Cells(r,c)hoặc aRange(cellAddress) cuộc gọi cuộc gọi. Hơn nữa, như đã lưu ý trên wiki này, điều này có thể được kết hợp với giá trị màu âm để đánh golf kích thước của các tham chiếu màu

Tham khảo nhanh

Tham chiếu phạm vi

Tế bào A1 có thể được tham chiếu theo bất kỳ cách nào sau đây

Range("A1")
Cells(1,1)
[A1]

và Phạm vi A1:D4có thể được tham chiếu theo bất kỳ cách nào sau đây

Range("A1:D4")
Range("A1").Resize(4,4)
Cells(1,1).Resize(4,4)
[A1].Resize(4,4,)
[A1:D4]

Tham khảo màu sắc

Black  0
White  -1
Red    255
Aqua   -256

3

Đơn giản hóa các chức năng tích hợp

Khi sử dụng một số chức năng nhất định thường xuyên, hãy gán lại chúng cho một chức năng do người dùng xác định.

Mã sau đây ( 127 ký tự) có thể được giảm từ:

Sub q()
w = 0
x = 1
y = 2
z = 3
a = Format(w, "0.0%")
b = Format(x, "0.0%")
c = Format(y, "0.0%")
d = Format(z, "0.0%")
End Sub

đến ( 124 ký tự):

Sub q()
w = 0
x = 1
y = 2
z = 3
a = f(w)
b = f(x)
c = f(y)
d = f(z)
End Sub
Function f(g)
f = Format(g, "0.0%")
End Function

Kết hợp điều này với thủ thuật ByRef , bạn có thể lưu nhiều ký tự hơn nữa (xuống còn 114 ):

Sub q()
w = 0
x = 1
y = 2
z = 3
a = f(w)
b = f(x)
c = f(y)
d = f(z)
End Sub
Sub f(g)
g = Format(g, "0.0%")
End Sub

Làm điều này một cách thận trọng, vì VBA chiếm rất nhiều ký tự để xác định a Function. Khối mã thứ hai thực sự sẽ là lớn hơn so với lần đầu tiên với bất kỳ số ít Format()các cuộc gọi.


2
trong lần cuối cùng, bạn không cần cuộc gọi chuyển nhượng, nghĩa là a = f(w)có thể rút gọn thànhf w
Taylor Scott

3

STDIN và STDOUT

Nhập vào Subcác thường trình và Functions thông qua các biến đầu vào

Public Sub A(ByRef B as String)

Có thể giảm xuống

Sub a(b$) 

Các cuộc gọi PublicByRefmặc định là VBA và do đó ẩn, và có thể (hầu như) luôn bị loại bỏ.

Các loại $lực lượng theo nghĩa đen bphải là loại String.

Loại chữ khác

  • ! Độc thân
  • @ Tiền tệ
  • # Gấp đôi
  • % Số nguyên
  • $ Chuỗi
  • & Dài
  • ^ LongLong (Chỉ 64 bit)

Hơn nữa, thông thường bạn chấp nhận rằng bạn có thể để biến đầu vào là loại mặc định Variantvà không xử lý bất kỳ lỗi dựa trên loại nào. Ví dụ. Sub E(F)trong đó Fdự kiến ​​sẽ thuộc loại Boolean[](sẽ được chuyển cho thói quen như E Array(True, False, False))

Nhập vào Subcác thói quen và chức năng cửa sổ ngay lập tức thông quaCells

VBA không có bảng điều khiển đầy đủ chức năng và do đó không có STDIN chính thức , và do đó cho phép một số trò chơi với việc chuyển đầu vào.

Trong excel, thường được chấp nhận để lấy đầu vào từ một ô hoặc phạm vi ô, có thể được thực hiện như

s=[A1]

mà ngầm đặt .valuetừ ô [A1](cũng có thể được tham chiếu là cells(1,1)hoặcrange("A1")

Vấn đề ví dụ: Hiển thị đầu vào trong hộp thông báo

Thông qua chương trình con Sub A:msgbox[A1]:End Sub

Thông qua chức năng cửa sổ Immediates msgbox[A1]

Nhập thông qua các đối số biên dịch có điều kiện

Các dự án VBA hỗ trợ lấy các đối số từ dòng lệnh hoặc thông qua Thuộc tính VBAProject (xem qua trình khám phá dự án -> [Dự án VBA của bạn] - (Nhấp chuột phải) -> Thuộc tính VBAProject -> Đối số biên dịch có điều kiện)

Điều này phần lớn hữu ích cho thách thức mã lỗi

Đưa ra đối số biên dịch có điều kiện n=[some_value], điều này cho phép thực thi mã sẽ tạo ra mã lỗi, dựa trên giá trị của n. lưu ý, điều này yêu cầu thêm 2 byte vào mã của bạn n=trong phần đối số biên dịch có điều kiện của Ngăn Thuộc tính VBAProject.

Mã ví dụ

...
#If n=3 then
return ''  Produces error code '3', Return without GoSub
#ElseIf n=20 then
resume ''  Produces error code '20', Resume without Error
#EndIf
...

Xuất ra giá trị chức năng

Không có gì nhiều để nói ở đây, hình thức chung của trích dẫn dưới đây là nhỏ gọn như nó có thể được thực hiện.

Public Function A(b)
    ...
    A=C
End Function

LƯU Ý: trong phần lớn các trường hợp, nhiều byte chuyển đổi phương thức sang chương trình con và xuất ra cửa sổ VBE tức thì (xem bên dưới)

Xuất ra từ Subcác thói quen và Functions thông qua Cửa sổ ngay lập tức VBE

Xuất ra cửa sổ VBE tức thì (AKA Cửa sổ gỡ lỗi VBE) là một phương thức đầu ra phổ biến cho VBA cho các thách thức dựa trên văn bản, tuy nhiên, điều quan trọng cần nhớ là Debug.Print "Text"cuộc gọi có thể bị đánh gôn.

Debug.Print "Text"

có chức năng giống hệt như

Debug.?"Text"

như ?autoformats để Print.

Xuất ra từ Subcác thói quen và VBE Immediates Các chức năng của Window thông qua các Phương thức khác

Trong trường hợp hiếm hoi , khi tình huống vừa phải, bạn có thể lấy đầu vào từ một số đầu vào tầm thường hơn có sẵn cho VBA như bộ điều chỉnh kích thước phông chữ, bộ chọn phông chữ và thu phóng. (Ví dụ: Mô phỏng Bộ chọn Kích thước Phông chữ )


2

Lưu ý nhanh về định dạng

Bởi vì StackExchange sử dụng MarkdownPrettify.js nên có thể thêm cờ ngôn ngữ vào câu trả lời mã hóa của bạn, điều này thường làm cho chúng trông chuyên nghiệp hơn. Mặc dù tôi không thể đảm bảo rằng điều này sẽ giúp bạn chơi golf tốt hơn trong VBA, nhưng tôi có thể đảm bảo rằng nó sẽ khiến bạn trông giống như bạn.

Thêm một trong các cờ bên dưới sẽ biến đổi

Public Sub a(ByRef b As Integer) ' this is a comment

đến

Public Sub a(ByRef b As Integer) ' this is a comment

Thẻ ngôn ngữ VBA

<!-- language: lang-vb -->

<!-- language-all: lang-vb -->

Lưu ý: cái sau biến đổi tất cả các đoạn mã trong câu trả lời của bạn, trong khi cái trước chỉ biến đổi các đoạn mã ngay sau


lang-vbHở? - Lạ rằng định dạng đó tốt hơn lang-vbacho câu trả lời vba.
Greedo

1
@Greedo, đó là bởi vì trong khi lang-vbasẽ cung cấp tô sáng, thực ra nó chỉ là tô sáng mặc định. Rõ ràng VBA chưa thực sự được triển khai trong Prettify.js, vì vậy chúng tôi sẽ phải gắn bó với phần tô sáng VB
Taylor Scott

2

Nhiều If .. Thenkiểm tra

Giống như trong các ngôn ngữ khác, nhiều Ifkiểm tra thường có thể được kết hợp thành một dòng duy nhất, cho phép sử dụng And/ Or(tức là &&/ ||trong C và các ngôn ngữ khác), trong VBA thay thế cả a Thenvà an End If.

Ví dụ: với một điều kiện lồng nhau ( 93 ký tự):

'There are MUCH easier ways to do this check (i.e. a = d).
'This is just for the sake of example.
If a = b Then
    If b = c Then
        If c = d Then
            MsgBox "a is equal to d"
        End If
    End If
End If

có thể trở thành ( 69 ký tự):

If a = b And b = c And c = d Then
    MsgBox "a is equal to d"
End If

Điều này cũng hoạt động với các điều kiện không lồng nhau.

Xem xét ( 84 ký tự):

If a = b Then            
    d = 0
End If

If c = b Then            
    d = 0
End If

Điều này có thể trở thành ( 51 ký tự):

If a = b Or c = b Then            
    d = 0
End If

1
trong If - thì báo cáo phần "Kết thúc - nếu" là tùy chọn cung cấp cho bạn viết mã trong một dòng duy nhất.
Rohan

@newguy Đúng. Xem thêm câu trả lời khác này: codegolf.stackexchange.com/a/5788/3862
Gaffi

Tuyên bố trên có thể được cô đọng hơn nữad=iif(a=b or c=b,0,d)
Taylor Scott

2

Kết thúc Forvòng

Khi sử dụng Forcác vòng lặp, aNext line does not need a variable name (though it is probably better to use in normal coding).

Vì thế,

For Variable = 1 to 100
    'Do stuff
Next Variable

có thể rút ngắn thành:

For Variable = 1 to 100
    'Do stuff
Next

(The savings depends on your variable name, though if you're golfing, that's probably just 1 character + 1 space.)


1
2 characters, don't forget to count the space!
11684

I happen to almost never identify the variable, even in normal code!
dnep

2

Split a string into a character array

Sometimes it can be useful to break apart a string into individual characters, but it can take a bit of code to do this manually in VBA.

ReDim a(1 To Len(s))
' ReDim because Dim can't accept non-Const values (Len(s))
For i = 1 To Len(s)
    a(i) = Mid(s, i, 1)
Next

Thay vào đó, bạn có thể sử dụng một dòng duy nhất, chuỗi chức năng tương đối tối thiểu để hoàn thành công việc:

a = Split(StrConv(s, 64), Chr(0))

Điều này sẽ gán chuỗi của bạn svào Variantmảng a. Tuy nhiên, hãy cẩn thận, vì mục cuối cùng trong mảng sẽ là một chuỗi rỗng ( ""), sẽ cần được xử lý một cách thích hợp.

Đây là cách nó hoạt động: StrConvHàm chuyển đổi một Stringđịnh dạng khác mà bạn chỉ định. Trong trường hợp này, 64 = vbUnicode, do đó, nó chuyển đổi sang định dạng unicode. Khi xử lý các chuỗi ASCII đơn giản, kết quả là một ký tự null (không phải là một chuỗi rỗng "") , được chèn sau mỗi ký tự.

Sau đây Splitsẽ chuyển đổi kết quả Stringthành một mảng, sử dụng ký tự null Chr(0)làm dấu phân cách.

It is important to note that Chr(0) is not the same as the empty string "", and using Split on "" will not return the array you might expect. The same is also true for vbNullString (but if you're golfing, then why would you use such a verbose constant in the first place?).


You should mention that the resulting array has an additional empty string at the end.
Howard

@Howard Yes, I should. It was in my head while I was typing this up, must have slipped my mind.
Gaffi

2

Using With (Sometimes! See footnote)

Using the With statement can reduce your code size significantly if you use some objects repeatedly.

i.e. this (80 chars):

x = foo.bar.object.a.value
y = foo.bar.object.b.value
z = foo.bar.object.c.value

can be coded as (79 chars):

With foo.bar.object
    x = .a.value
    y = .b.value
    z = .c.value
End With

The above isn't even the best-case scenario. If using anything with Application, such as Excel.Application from within Access, the improvement will be much more significant.


*Depending on the situation, With may or may not be more efficient than this (64 chars):

Set i = foo.bar.object
x = i.a.value
y = i.b.value
z = i.c.value

2

Infinite Loops

Consider replacing

Do While a<b
'...
Loop

or

Do Until a=b
'...
Loop

with the antiquated but lower byte count

While a<b
'...
Wend

If you need to exit a Sub or Function prematurely, then instead of Exit ...

For i=1To 1000
    If i=50 Then Exit Sub
Next

consider End

For i=1To 1E3
    If i=50 Then End
Next

Note that End halts all code execution and clears out any global variables, so use wisely


You should consider rewriting the last section (For and End) to reflect that the case 100 will never be met and adds an unecessary byte
Taylor Scott

Also, while this is very legible, you may consider removing the whitespace to show the possible benefits of these methods better (it is always good to abuse the autoformatting in VBA for codegolfing)
Taylor Scott

2

Use Spc(n) over Space(n), [Rept(" ",n)] or String(n," ")

When trying to insert several spaces of length n, use

Spc(n)              ''  6  bytes

over

B1=n:[Rept(" ",B1)] ''  19 bytes
String(n," ")       ''  13 bytes
Space(n)            ''  8  bytes

Note: I am not sure why, but it seems that you cannot assign the output of Spc(n) to a variable, and that it must rather be printed directly - so in certain cases Space(n) is still the best way to go.


2

Reduce Debug.Print and Print calls

Debug.Print [some value] 

(12 Bytes; note the trailing space) may be reduced to

Debug.?[some value]

(7 Bytes; note the lack of trailing space).

Similarly,

Print 

(6 Bytes) may be reduced to

?

(1 Byte).

Furthermore, when operating in the context of an anonymous VBE immediate window function the Debug statement may be dropped entirely, and instead printing to the VBE immediate window via ? may be assumed to be STDIN/STDOUT

Special characters

When printing strings you can also use special characters in place of &. These allow for alternate formats in what's printed or whitespace removal

To join variable strings, instead of

Debug.Print s1 &s2

You can use a semicolon ;

Debug.?s1;s2

This semicolon is the default behaviour for consecutive strings, as long as there is no ambiguity So these are valid:

Debug.?s1"a"
Debug.?"a"s1

But not

Debug.?s1s2 'as this could be interpreted as a variable named "s1s2", not 2 variables
            'use Debug.?s1 s2 instead (or s1;s2)
Debug.?"a""b" 'this prints a"b, so insert a space or ; for ab

Note that a ; at the end of a line suppresses a newline, as newlines are by default added after every print. Counting bytes returned by VBA code is under debate

To join with a tab use a comma , (actually 14 spaces, but according to )

Debug.?s1,s2 'returns "s1              s2"

Finally, you can use type declarations to join strings instead of ;, each having its own slight effect on formatting. For all of the below a = 3.14159 is the first line

Debug.?a&"is pi" -> " 3 is pi" 'dims a as long, adds leading and trailing space to a
Debug.?a!"is pi" -> " 3.14159 is pi" 'dims a as single, adds leading and trailing space
Debug.?a$"is pi" -> "3.14159is pi" 'dims a as string, no spaces added

2

Use a helper Sub to Print

If the code requires using more than six Debug.? statements you can reduce bytes by replacing all Debug.? lines with a call to a Sub like the one below. To print a blank line you need to call p "" since a parameter is required.

Sub p(m)
Debug.?m
End Sub

1
To print a new line with that you would only need p"" or if it can be unterminated, p". However, in the vast majority of cases where this may apply, it makes more sense to use a string variable and only print the string variable at the end.
Taylor Scott

1

Use Array() Choose() instead of Select or If...Then

When assigning a variable based on a the value of another variable, it makes sense to write out the steps with If...Then checks, like so:

If a = 1 Then
b = "a"
ElseIf a = 2 Then
b = "c"
'...
End If

However, this can take up a lot of code space if there are more than one or two variables to check (there are still generally better ways to do even that anyway).

Instead, the Select Case statement helps reduce the size of the checks by encasing everything in one block, like so:

Select Case a
Case 1:
b = "a"
Case 2:
b = "c"
'...
End Select

This can lead to much smaller code, but for very simple cases such as this, there is an even more efficient method: Choose() This function will pick a value from a list, based on the value passed to it.

b = Choose(a,"a","c",...)

The option to select (a in this case) is an integer value passed as the first argument. All subsequent arguments are the values to choose from (1-indexed, like most VBA). The values can be any data type so long as it matches the variable being set (i.e. objects don't work without the Set keyword) and can even be expressions or functions.

b = Choose(a, 5 + 4, String(7,"?"))

An additional option is to use the Array function to get the same effect while saving another character:

b = Array(5 + 4, String(7,"?"))(a)

An interesting and possibly very beneficial use for golfing is to use this in conjunction with an IIf() or another Choose(): A1=IIf(a=Choose(b,c,d,e),Choose(Choose(f,g,h,i),j,k,l),Choose(IIf(m=n,Choose(p,q,r,s),IIf(t=u,v,w)),x,y,z))
Gaffi

1

Bit-Shifted RGB Values

Because of the way that Excel handles colors, (unsigned, 6-character hexadecimal integer) you can make use of negatively signed integers, of which excel will only use the right 6 bytes to assign a color value

This is a bit confusing so some examples are provided below

Lets say you want to use the color white, which is stored as

rgbWhite

and is equivalent to

&HFFFFFF ''#value: 16777215

to golf this down all we need to do is find a negative hexadecimal value which terminates in FFFFFF and since negative Hex values in must be in the format of FFFFXXXXXX (such that X is a valid hexadecimal) counting down from FFFFFFFFFF (-1) to FFFF000001 (-16777215)

Knowing this we can generalize that the formula

Let Negative_Color_Value = -rgbWhite + Positive_Color_Value - 1
                         = -16777215 + Positive_Color_Value - 1
                         = -16777216 + Positive_Color_Value

Using this, some useful conversions are

rgbWhite = &HFFFFFF = -1 
rgbAqua  = &HFFFF00 = -256
16747627 = &HFF8C6B = -29589

Good to know what was going on there, I just discovered it by accident in that question - but this clear explanation shows exactly how it works. Another thing to bear in mind is that certain key colours can be reduced with mathematical operators; rgbWhite=2^24-1 although could possibly be reduced further if you miss off the -1 to get roughly there
Greedo

1
In fact, here is a list of colours with their mathematical representations, which are shorter than either the positive or negative decimal versions: &000080: 2^7, &000100: 2^8, &000200: 2^9, &000400: 2^10, &000800: 2^11, &001000: 2^12, &002000: 2^13, &004000: 2^14, &008000: 2^15, &010000: 2^16, &01FFFF: 2^17-1, &020000: 2^17, &03FFFF: 2^18-1, &040000: 2^18, &07FFFF: 2^19-1, &080000: 2^19, &0FFFFF: 2^20-1, &100000: 2^20, &1FFFFF: 2^21-1, &3FFFFF: 2^22-1, &400000: 2^22, &7FFFFF: 2^23-1 n.b. not necessarily complete, but I think I did a fairly thorough job
Greedo

1

Omit terminal " when printing to Immediate Window

Given the function below, which is to be used in the debug window

h="Hello":?h", World!"

The Terminal " may be dropped for -1 Byte, leaving the string unclosed and the program shall execute the same, without error

h="Hello":?h", World!

Even more surprising than this is that this may be done within fully defined subroutines.

Sub a
h="Hello":Debug.?h", World!
End Sub

The subroutine above runs as expected and autoformats to

Sub a()
h = "Hello": Debug.Print h; ", World!"
End Sub

1

Use Truthy and Falsey Variables in conditionals

Sometimes called implicit type conversion - directly using a number type in a If,[IF(...)] or IIf(...) statement, by using the truthy and falsey nature of that number can absolutely save you some bytes.

In VBA, any number type variable that is non-zero is considered to be truthy ( and thus zero, 0, is the only value falsey) and thus

If B <> 0 Then 
    Let C = B
Else 
    Let C = D
End If 

may be condensed to

If B Then C=B Else C=D

and further to

C=IIf(B,B,D)

1

Address Sheets By Name

Instead of Addressing a WorkSheet object by calling

Application.ActiveSheet
''  Or 
ActiveSheet   

One may use

Sheets(n)  ''  Where `n` is an integer
''  Or 
[Sheet1]

Or More preferably one may access the object directly and use

Sheet1

1

Exponentiation and LongLongs in 64-Bit VBA

The general form of exponentiation,

A to the power of B

Can be represented as in VBA as

A^B 

But Only in 32-Bit Installs of Office, in 64-Bits installs of Office, the shortest way that you may represent without error this is

A ^B

This is because in 64-Bit versions of VBA ^ serves as both the exponentiation literal and the LongLong type declaration character. This means that some rather odd-looking but valid syntax can arise such as

a^=2^^63^-1^

which assigns variable a to hold the max value of a Longlong


1

Compress Byte Arrays as String

For challanges that require the solution to hold constant data, it shall may be useful to compress that data as a single string and to then iterate across the string to fetch the data.

In VBA, any byte value may be directly converted to a character with

Chr(byteVal)

And a long or integer value may be converted to two characters with

Chr(longVal / 256) & Chr(longVal Mod 256)

So for a byte array a compression algorithm would look something like

For Each Var In bytes
    Select Case Var
        Case Is = Asc(vbNullChar)
            outstr$ = outstr$ & """+chr(0)+"""
        Case Is = Asc(vbTab)
            outstr$ = outstr$ & """+vbTab+"""
        Case Is = Asc("""")
            outstr$ = outstr$ & """"""
        Case Is = Asc(vbLf)
            outstr$ = outstr$ & """+vbLf+"""
        Case Is = Asc(vbCr)
            outstr$ = outstr$ & """+vbCr+"""
        Case Else
            outstr$ = outstr$ & Chr$(Var)
    End Select
Next
Debug.Print "t="""; outstr$

Noting that the Select Case section is implemented due to the characters which may not be stored as literals in the string.

From the resulting string, which will look something like

t="5¼-™):ó™ˆ"+vbTab+"»‘v¶<®Xn³"+Chr(0)+"~ίšÐ‘š;$ÔÝ•óŽ¡¡EˆõW'«¡*{ú{Óx.OÒ/R)°@¯ˆ”'®ïQ*<¹çu¶àªp~ÅP>‹:<­«a°;!¾y­›/,”Ì#¥œ5*B)·7    

The data may then be extracted using the Asc and Mid functions, eg

i=Asc(Mid(t,n+1))

1

Use Improper Constants

VBA allows, in some cases, for the use of unlisted or improper constants in expressions which require constant values. These are often legacy values, and shorter in length than the proper values.

For example the values below may be used when letting a value be held by the rng.HorizantalAlignment property.

ImproperProperGeneral ConstantxlHAlign  Constant11xlGeneralxlHAlignGeneral24131xlLeftxlHAlignLeft34108xlCenterxlHAlignCenter44152xlRightxlHAlignRight55xlFillxlHAlignFill64130xlJustifyxlHAlignJustify77xlCenterAcrossSelectionxlHAlignCenterAcrossSelection84117xlDistributedxlHAlignDistributed

This means, for example that setting a cell to having its text centered with

rng.HorizantalAlignment=-4108

may be shortened down to

rng.HorizantalAlignment=3

by replacing the improper constant value 3 for the proper value -4108. Note that if you fetch the rng.HorizantalAlignment property value, it will return the proper value. In this case, that would be -4108.


1

In Excel, Create Numeric Arrays by Coercing Ranges

When a constant single dimensional set of numerics of mixed length, S is needed, it shall be more efficient to use [{...}] notation to declare a range and then coerce that into a numeric array rather than to use the Split(String) notation or similar.

Using this method, a change of Δbyte count=5 bytes shall be accrued for all S.

Example

For instance,

x=Split("1 2 34 567 8910")

may be written as

x=[{1,2,34,567,8910}]

It is further worth noting that while this method does not allow for direct indexing, it does allow for iteration over the for loop directly, ie

For Each s In[{1,3,5,7,9,2,4,6,8}]
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.