Skip to content
This repository was archived by the owner on Feb 25, 2023. It is now read-only.

Latest commit

History

67 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Project Status: Abandoned – Initial development has started, but there has not yet been a stable, usable release; the project has been abandoned and the author(s) do not intend on continuing development.

This library is abandonned by the original author. A fork is maintained at https://github.com/Lisp-Stat/array-operations.

NOTE 2019-05-16

@bendudson and others have continued maintaining a fork of this library at https://github.com/bendudson/array-operations.

!!! important This is an alpha release. All the code works and unit tests are expected to run perfectly, but the operations are not optimized and the API change.

Introduction

array-operations is a Common Lisp library that facilitates working with Common Lisp arrays using syntax and semantics that work well with the rest of the language.

The library previously available under this name is deprecated, but you can find it here.

A quick tour of the library

Shorthand for frequently used Common Lisp array functions


The library defines the following short function names that are synomyms for Common Lisp operations:

array-operationsCommon Lisp
sizearray-total-size
rankarray-rank
dimarray-dimension
dimsarray-dimensions
nrownumber of rows in matrix
ncolnumber of columns in matrix

The array-operations package has the nickname ao, so you can use, for example, (ao:size my-array) without use'ing the package.

Displaced arrays for fun and profit

displaced array n. an array which has no storage of its own, but which is instead indirected to the storage of another array, called its target, at a specified offset, in such a way that any attempt to access the displaced array implicitly references the target array. (CLHS Glossary)

Displaced arrays are one of the niftiest features of Common Lisp. When an array is displaced to another array, it shares structure with (part of) that array. The two arrays do not need to have the same dimensions, in fact, the dimensions do not be related at all as long as the displaced array fits inside the original one. The row-major index of the former in the latter is called the offset of the displacement.

Displaced arrays are usually constructed using make-array, but this library also provides displace for that purpose:

(defparameter*a*#2A((123) (456)))
(ao:displace *a*21) ; => #(2 3)

flatten displaces to a row-major array:

(ao:flatten *a*) ; => #(1 2 3 4 5 6)

The real fun starts with split, which splits off subarrays nested within a given axis:

(ao:split *a*1) ; => #(#(1 2 3) #(4 5 6))
(defparameter*b*#3A(((01) (23))
((45) (67))))
(ao:split *b*0) ; => #3A(((0 1) (2 3)) ((4 5) (6 7)))
(ao:split *b*1) ; => #(#2A((0 1) (2 3)) #2A((4 5) (6 7)))
(ao:split *b*2) ; => #2A((#(0 1) #(2 3)) (#(4 5) #(6 7)))
(ao:split *b*3) ; => #3A(((0 1) (2 3)) ((4 5) (6 7)))

Note how splitting at 0 and the rank of the array returns the array itself.

Now consider sub, which returns a specific array, composed of the elements that would start with given subscripts:

(ao:sub *b*0) ; => #2A((0 1) (2 3))
(ao:sub *b*01) ; => #(2 3)
(ao:sub *b*010) ; => 2

There is also a (setf sub) function.

partition returns a consecutive chunk of an array separated along its first subscript:

(ao:partition #2A((01)
(23)
(45)
(67)
(89))
13) ; => #2A((2 3) (4 5))

and also has a (setf partition) pair.

combine is the opposite of split:

(ao:combine #(#(01) #(23))) ; => #2A((0 1) (2 3))

subvec returns a displaced subvector:

(ao:subvec #(01234) 24) ; => #(2 3)

There is also a (setf subvec) function, which is like (setf subseq) except for demanding matching lengths.

Finally, reshape can be used to displace arrays into a different shape:

(ao:reshape *a*'(32)) ; => #2A((1 2) (3 4) (5 6))

You can use t for one of the dimensions, to be filled in automatically:

(ao:reshape *b*'(1t)) ; => #2A((0 1 2 3 4 5 6 7))

reshape-col and reshape-row reshape your array into a column or row matrix, respectively.

Dimension specifications

Functions in the library accept the following in place of dimensions:

  • a list of dimensions (as for make-array),
  • a positive integer, which is used as a single-element list,
  • another array, the dimensions of which are used.

The last one allows you to specify dimensions with other arrays. For example, to reshape an array a1 to look like a2, you can use

(ao:reshape a1 a2)

instead of the longer form

(ao:reshape a1 (ao:dims a2))

Array creation and transformations

When the resulting element type cannot be inferred, functions that create and transform arrays are provided in pairs: one of these will allow you to specify the array-element-type of the result, while the other assumes it is t. The former ends with a *, and the element-type is always its first argument. I give examples for the versions without *, use the other when you are optimizing your code and you are sure you can constrain to a given element-type.

Element traversal order of these functions is unspecified. The reason for this is that the library may use parallel code in the future, so it is unsafe to rely on a particular element traversal order.

generate (and generate*) allow you to generate arrays using functions.

(ao:generate (lambda () (random10)) 3) ; => #(6 9 5)
(ao:generate #'identity'(23) :position) ; => #2A((0 1 2) (3 4 5))
(ao:generate #'identity'(22) :subscripts)
;; => #2A(((0 0) (0 1)) ((1 0) (1 1)))
(ao:generate #'cons'(22) :position-and-subscripts)
;; => #2A(((0 0 0) (1 0 1)) ((2 1 0) (3 1 1)))

Depending on the last argument, the function will be called with the (row-major) position, the subscripts, both, or no argument.

permute can permutate subscripts (you can also invert, complement, and complete permutations, look at the docstring and the unit tests). Transposing is a special case of permute:

(ao:permute '(01) *a*) ; => #2A((1 2 3) (4 5 6))

each applies a function to its (array) arguments elementwise:

(ao:each #'+#(012) #(235)) ; => #(2 4 7)

The semantics of margin are more difficult to explain, so perhaps an example will be more useful. Suppose that you want to calculate column sums in a matrix. You could permute (transpose) the matrix, split its subarrays at rank one (so you get a vector for each row), and apply the function that calculates the sum. margin automates that for you:

(ao:margin (lambda (column)
(reduce#'+ column))
#2A((01)
(23)
(57)) 0) ; => #(7 11)

But the function is much more general than this: the arguments inner and outer allow arbitrary permutations before splitting.

Finally, recycle allows you to recycle arrays along inner and outer dimensions:

(ao:recycle #(23) :inner2:outer4)
; => #3A(((2 2) (3 3)) ((2 2) (3 3)) ((2 2) (3 3)) ((2 2) (3 3)))

Scalars as 0-dimensional arrays

Library functions treat non-array objects as if they were equivalent to 0-dimensional arrays: for example, (ao:split array (rank array)) returns an array that effectively equivalent (eq) to array. Another example is recycle:

(ao:recycle 4:inner'(22)) ; => #2A((4 4) (4 4))

Stacking

You can also stack compatible arrays along any axis:

(defparameter*a1*#(012))
(defparameter*a2*#(357))
(ao:stack 0*a1**a2*) ; => #(0 1 2 3 5 7)
(ao:stack 1
(ao:reshape-col *a1*)
(ao:reshape-col *a2*)) ; => #2A((0 3) (1 5) (2 7))

Shared structure

Rules for that aren't finalized yet, see the source. Suggestions are welcome.

To-do list

benchmark and optimize walk-subscripts and walk-subscripts-list

  • instead of allocating a new list each time, could map into a preallocated one

About

Common Lisp library that facilitates working with Common Lisp arrays.

Resources

Stars

37 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - tpapp/array-operations: Common Lisp library that facilitates working with Common Lisp arrays. · GitHub
Skip to content
This repository was archived by the owner on Feb 25, 2023. It is now read-only.

Latest commit

History

67 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Project Status: Abandoned – Initial development has started, but there has not yet been a stable, usable release; the project has been abandoned and the author(s) do not intend on continuing development.

This library is abandonned by the original author. A fork is maintained at https://github.com/Lisp-Stat/array-operations.

NOTE 2019-05-16

@bendudson and others have continued maintaining a fork of this library at https://github.com/bendudson/array-operations.

!!! important This is an alpha release. All the code works and unit tests are expected to run perfectly, but the operations are not optimized and the API change.

Introduction

array-operations is a Common Lisp library that facilitates working with Common Lisp arrays using syntax and semantics that work well with the rest of the language.

The library previously available under this name is deprecated, but you can find it here.

A quick tour of the library

Shorthand for frequently used Common Lisp array functions


The library defines the following short function names that are synomyms for Common Lisp operations:

array-operationsCommon Lisp
sizearray-total-size
rankarray-rank
dimarray-dimension
dimsarray-dimensions
nrownumber of rows in matrix
ncolnumber of columns in matrix

The array-operations package has the nickname ao, so you can use, for example, (ao:size my-array) without use'ing the package.

Displaced arrays for fun and profit

displaced array n. an array which has no storage of its own, but which is instead indirected to the storage of another array, called its target, at a specified offset, in such a way that any attempt to access the displaced array implicitly references the target array. (CLHS Glossary)

Displaced arrays are one of the niftiest features of Common Lisp. When an array is displaced to another array, it shares structure with (part of) that array. The two arrays do not need to have the same dimensions, in fact, the dimensions do not be related at all as long as the displaced array fits inside the original one. The row-major index of the former in the latter is called the offset of the displacement.

Displaced arrays are usually constructed using make-array, but this library also provides displace for that purpose:

(defparameter*a*#2A((123) (456)))
(ao:displace *a*21) ; => #(2 3)

flatten displaces to a row-major array:

(ao:flatten *a*) ; => #(1 2 3 4 5 6)

The real fun starts with split, which splits off subarrays nested within a given axis:

(ao:split *a*1) ; => #(#(1 2 3) #(4 5 6))
(defparameter*b*#3A(((01) (23))
((45) (67))))
(ao:split *b*0) ; => #3A(((0 1) (2 3)) ((4 5) (6 7)))
(ao:split *b*1) ; => #(#2A((0 1) (2 3)) #2A((4 5) (6 7)))
(ao:split *b*2) ; => #2A((#(0 1) #(2 3)) (#(4 5) #(6 7)))
(ao:split *b*3) ; => #3A(((0 1) (2 3)) ((4 5) (6 7)))

Note how splitting at 0 and the rank of the array returns the array itself.

Now consider sub, which returns a specific array, composed of the elements that would start with given subscripts:

(ao:sub *b*0) ; => #2A((0 1) (2 3))
(ao:sub *b*01) ; => #(2 3)
(ao:sub *b*010) ; => 2

There is also a (setf sub) function.

partition returns a consecutive chunk of an array separated along its first subscript:

(ao:partition #2A((01)
(23)
(45)
(67)
(89))
13) ; => #2A((2 3) (4 5))

and also has a (setf partition) pair.

combine is the opposite of split:

(ao:combine #(#(01) #(23))) ; => #2A((0 1) (2 3))

subvec returns a displaced subvector:

(ao:subvec #(01234) 24) ; => #(2 3)

There is also a (setf subvec) function, which is like (setf subseq) except for demanding matching lengths.

Finally, reshape can be used to displace arrays into a different shape:

(ao:reshape *a*'(32)) ; => #2A((1 2) (3 4) (5 6))

You can use t for one of the dimensions, to be filled in automatically:

(ao:reshape *b*'(1t)) ; => #2A((0 1 2 3 4 5 6 7))

reshape-col and reshape-row reshape your array into a column or row matrix, respectively.

Dimension specifications

Functions in the library accept the following in place of dimensions:

  • a list of dimensions (as for make-array),
  • a positive integer, which is used as a single-element list,
  • another array, the dimensions of which are used.

The last one allows you to specify dimensions with other arrays. For example, to reshape an array a1 to look like a2, you can use

(ao:reshape a1 a2)

instead of the longer form

(ao:reshape a1 (ao:dims a2))

Array creation and transformations

When the resulting element type cannot be inferred, functions that create and transform arrays are provided in pairs: one of these will allow you to specify the array-element-type of the result, while the other assumes it is t. The former ends with a *, and the element-type is always its first argument. I give examples for the versions without *, use the other when you are optimizing your code and you are sure you can constrain to a given element-type.

Element traversal order of these functions is unspecified. The reason for this is that the library may use parallel code in the future, so it is unsafe to rely on a particular element traversal order.

generate (and generate*) allow you to generate arrays using functions.

(ao:generate (lambda () (random10)) 3) ; => #(6 9 5)
(ao:generate #'identity'(23) :position) ; => #2A((0 1 2) (3 4 5))
(ao:generate #'identity'(22) :subscripts)
;; => #2A(((0 0) (0 1)) ((1 0) (1 1)))
(ao:generate #'cons'(22) :position-and-subscripts)
;; => #2A(((0 0 0) (1 0 1)) ((2 1 0) (3 1 1)))

Depending on the last argument, the function will be called with the (row-major) position, the subscripts, both, or no argument.

permute can permutate subscripts (you can also invert, complement, and complete permutations, look at the docstring and the unit tests). Transposing is a special case of permute:

(ao:permute '(01) *a*) ; => #2A((1 2 3) (4 5 6))

each applies a function to its (array) arguments elementwise:

(ao:each #'+#(012) #(235)) ; => #(2 4 7)

The semantics of margin are more difficult to explain, so perhaps an example will be more useful. Suppose that you want to calculate column sums in a matrix. You could permute (transpose) the matrix, split its subarrays at rank one (so you get a vector for each row), and apply the function that calculates the sum. margin automates that for you:

(ao:margin (lambda (column)
(reduce#'+ column))
#2A((01)
(23)
(57)) 0) ; => #(7 11)

But the function is much more general than this: the arguments inner and outer allow arbitrary permutations before splitting.

Finally, recycle allows you to recycle arrays along inner and outer dimensions:

(ao:recycle #(23) :inner2:outer4)
; => #3A(((2 2) (3 3)) ((2 2) (3 3)) ((2 2) (3 3)) ((2 2) (3 3)))

Scalars as 0-dimensional arrays

Library functions treat non-array objects as if they were equivalent to 0-dimensional arrays: for example, (ao:split array (rank array)) returns an array that effectively equivalent (eq) to array. Another example is recycle:

(ao:recycle 4:inner'(22)) ; => #2A((4 4) (4 4))

Stacking

You can also stack compatible arrays along any axis:

(defparameter*a1*#(012))
(defparameter*a2*#(357))
(ao:stack 0*a1**a2*) ; => #(0 1 2 3 5 7)
(ao:stack 1
(ao:reshape-col *a1*)
(ao:reshape-col *a2*)) ; => #2A((0 3) (1 5) (2 7))

Shared structure

Rules for that aren't finalized yet, see the source. Suggestions are welcome.

To-do list

benchmark and optimize walk-subscripts and walk-subscripts-list

  • instead of allocating a new list each time, could map into a preallocated one

About

Common Lisp library that facilitates working with Common Lisp arrays.

Resources

Stars

37 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - tpapp/array-operations: Common Lisp library that facilitates working with Common Lisp arrays. · GitHub
Skip to content
This repository was archived by the owner on Feb 25, 2023. It is now read-only.

Latest commit

History

67 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Project Status: Abandoned – Initial development has started, but there has not yet been a stable, usable release; the project has been abandoned and the author(s) do not intend on continuing development.

This library is abandonned by the original author. A fork is maintained at https://github.com/Lisp-Stat/array-operations.

NOTE 2019-05-16

@bendudson and others have continued maintaining a fork of this library at https://github.com/bendudson/array-operations.

!!! important This is an alpha release. All the code works and unit tests are expected to run perfectly, but the operations are not optimized and the API change.

Introduction

array-operations is a Common Lisp library that facilitates working with Common Lisp arrays using syntax and semantics that work well with the rest of the language.

The library previously available under this name is deprecated, but you can find it here.

A quick tour of the library

Shorthand for frequently used Common Lisp array functions


The library defines the following short function names that are synomyms for Common Lisp operations:

array-operationsCommon Lisp
sizearray-total-size
rankarray-rank
dimarray-dimension
dimsarray-dimensions
nrownumber of rows in matrix
ncolnumber of columns in matrix

The array-operations package has the nickname ao, so you can use, for example, (ao:size my-array) without use'ing the package.

Displaced arrays for fun and profit

displaced array n. an array which has no storage of its own, but which is instead indirected to the storage of another array, called its target, at a specified offset, in such a way that any attempt to access the displaced array implicitly references the target array. (CLHS Glossary)

Displaced arrays are one of the niftiest features of Common Lisp. When an array is displaced to another array, it shares structure with (part of) that array. The two arrays do not need to have the same dimensions, in fact, the dimensions do not be related at all as long as the displaced array fits inside the original one. The row-major index of the former in the latter is called the offset of the displacement.

Displaced arrays are usually constructed using make-array, but this library also provides displace for that purpose:

(defparameter*a*#2A((123) (456)))
(ao:displace *a*21) ; => #(2 3)

flatten displaces to a row-major array:

(ao:flatten *a*) ; => #(1 2 3 4 5 6)

The real fun starts with split, which splits off subarrays nested within a given axis:

(ao:split *a*1) ; => #(#(1 2 3) #(4 5 6))
(defparameter*b*#3A(((01) (23))
((45) (67))))
(ao:split *b*0) ; => #3A(((0 1) (2 3)) ((4 5) (6 7)))
(ao:split *b*1) ; => #(#2A((0 1) (2 3)) #2A((4 5) (6 7)))
(ao:split *b*2) ; => #2A((#(0 1) #(2 3)) (#(4 5) #(6 7)))
(ao:split *b*3) ; => #3A(((0 1) (2 3)) ((4 5) (6 7)))

Note how splitting at 0 and the rank of the array returns the array itself.

Now consider sub, which returns a specific array, composed of the elements that would start with given subscripts:

(ao:sub *b*0) ; => #2A((0 1) (2 3))
(ao:sub *b*01) ; => #(2 3)
(ao:sub *b*010) ; => 2

There is also a (setf sub) function.

partition returns a consecutive chunk of an array separated along its first subscript:

(ao:partition #2A((01)
(23)
(45)
(67)
(89))
13) ; => #2A((2 3) (4 5))

and also has a (setf partition) pair.

combine is the opposite of split:

(ao:combine #(#(01) #(23))) ; => #2A((0 1) (2 3))

subvec returns a displaced subvector:

(ao:subvec #(01234) 24) ; => #(2 3)

There is also a (setf subvec) function, which is like (setf subseq) except for demanding matching lengths.

Finally, reshape can be used to displace arrays into a different shape:

(ao:reshape *a*'(32)) ; => #2A((1 2) (3 4) (5 6))

You can use t for one of the dimensions, to be filled in automatically:

(ao:reshape *b*'(1t)) ; => #2A((0 1 2 3 4 5 6 7))

reshape-col and reshape-row reshape your array into a column or row matrix, respectively.

Dimension specifications

Functions in the library accept the following in place of dimensions:

  • a list of dimensions (as for make-array),
  • a positive integer, which is used as a single-element list,
  • another array, the dimensions of which are used.

The last one allows you to specify dimensions with other arrays. For example, to reshape an array a1 to look like a2, you can use

(ao:reshape a1 a2)

instead of the longer form

(ao:reshape a1 (ao:dims a2))

Array creation and transformations

When the resulting element type cannot be inferred, functions that create and transform arrays are provided in pairs: one of these will allow you to specify the array-element-type of the result, while the other assumes it is t. The former ends with a *, and the element-type is always its first argument. I give examples for the versions without *, use the other when you are optimizing your code and you are sure you can constrain to a given element-type.

Element traversal order of these functions is unspecified. The reason for this is that the library may use parallel code in the future, so it is unsafe to rely on a particular element traversal order.

generate (and generate*) allow you to generate arrays using functions.

(ao:generate (lambda () (random10)) 3) ; => #(6 9 5)
(ao:generate #'identity'(23) :position) ; => #2A((0 1 2) (3 4 5))
(ao:generate #'identity'(22) :subscripts)
;; => #2A(((0 0) (0 1)) ((1 0) (1 1)))
(ao:generate #'cons'(22) :position-and-subscripts)
;; => #2A(((0 0 0) (1 0 1)) ((2 1 0) (3 1 1)))

Depending on the last argument, the function will be called with the (row-major) position, the subscripts, both, or no argument.

permute can permutate subscripts (you can also invert, complement, and complete permutations, look at the docstring and the unit tests). Transposing is a special case of permute:

(ao:permute '(01) *a*) ; => #2A((1 2 3) (4 5 6))

each applies a function to its (array) arguments elementwise:

(ao:each #'+#(012) #(235)) ; => #(2 4 7)

The semantics of margin are more difficult to explain, so perhaps an example will be more useful. Suppose that you want to calculate column sums in a matrix. You could permute (transpose) the matrix, split its subarrays at rank one (so you get a vector for each row), and apply the function that calculates the sum. margin automates that for you:

(ao:margin (lambda (column)
(reduce#'+ column))
#2A((01)
(23)
(57)) 0) ; => #(7 11)

But the function is much more general than this: the arguments inner and outer allow arbitrary permutations before splitting.

Finally, recycle allows you to recycle arrays along inner and outer dimensions:

(ao:recycle #(23) :inner2:outer4)
; => #3A(((2 2) (3 3)) ((2 2) (3 3)) ((2 2) (3 3)) ((2 2) (3 3)))

Scalars as 0-dimensional arrays

Library functions treat non-array objects as if they were equivalent to 0-dimensional arrays: for example, (ao:split array (rank array)) returns an array that effectively equivalent (eq) to array. Another example is recycle:

(ao:recycle 4:inner'(22)) ; => #2A((4 4) (4 4))

Stacking

You can also stack compatible arrays along any axis:

(defparameter*a1*#(012))
(defparameter*a2*#(357))
(ao:stack 0*a1**a2*) ; => #(0 1 2 3 5 7)
(ao:stack 1
(ao:reshape-col *a1*)
(ao:reshape-col *a2*)) ; => #2A((0 3) (1 5) (2 7))

Shared structure

Rules for that aren't finalized yet, see the source. Suggestions are welcome.

To-do list

benchmark and optimize walk-subscripts and walk-subscripts-list

  • instead of allocating a new list each time, could map into a preallocated one

About

Common Lisp library that facilitates working with Common Lisp arrays.

Resources

Stars

37 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - tpapp/array-operations: Common Lisp library that facilitates working with Common Lisp arrays. · GitHub
Skip to content
This repository was archived by the owner on Feb 25, 2023. It is now read-only.

Latest commit

History

67 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Project Status: Abandoned – Initial development has started, but there has not yet been a stable, usable release; the project has been abandoned and the author(s) do not intend on continuing development.

This library is abandonned by the original author. A fork is maintained at https://github.com/Lisp-Stat/array-operations.

NOTE 2019-05-16

@bendudson and others have continued maintaining a fork of this library at https://github.com/bendudson/array-operations.

!!! important This is an alpha release. All the code works and unit tests are expected to run perfectly, but the operations are not optimized and the API change.

Introduction

array-operations is a Common Lisp library that facilitates working with Common Lisp arrays using syntax and semantics that work well with the rest of the language.

The library previously available under this name is deprecated, but you can find it here.

A quick tour of the library

Shorthand for frequently used Common Lisp array functions


The library defines the following short function names that are synomyms for Common Lisp operations:

array-operationsCommon Lisp
sizearray-total-size
rankarray-rank
dimarray-dimension
dimsarray-dimensions
nrownumber of rows in matrix
ncolnumber of columns in matrix

The array-operations package has the nickname ao, so you can use, for example, (ao:size my-array) without use'ing the package.

Displaced arrays for fun and profit

displaced array n. an array which has no storage of its own, but which is instead indirected to the storage of another array, called its target, at a specified offset, in such a way that any attempt to access the displaced array implicitly references the target array. (CLHS Glossary)

Displaced arrays are one of the niftiest features of Common Lisp. When an array is displaced to another array, it shares structure with (part of) that array. The two arrays do not need to have the same dimensions, in fact, the dimensions do not be related at all as long as the displaced array fits inside the original one. The row-major index of the former in the latter is called the offset of the displacement.

Displaced arrays are usually constructed using make-array, but this library also provides displace for that purpose:

(defparameter*a*#2A((123) (456)))
(ao:displace *a*21) ; => #(2 3)

flatten displaces to a row-major array:

(ao:flatten *a*) ; => #(1 2 3 4 5 6)

The real fun starts with split, which splits off subarrays nested within a given axis:

(ao:split *a*1) ; => #(#(1 2 3) #(4 5 6))
(defparameter*b*#3A(((01) (23))
((45) (67))))
(ao:split *b*0) ; => #3A(((0 1) (2 3)) ((4 5) (6 7)))
(ao:split *b*1) ; => #(#2A((0 1) (2 3)) #2A((4 5) (6 7)))
(ao:split *b*2) ; => #2A((#(0 1) #(2 3)) (#(4 5) #(6 7)))
(ao:split *b*3) ; => #3A(((0 1) (2 3)) ((4 5) (6 7)))

Note how splitting at 0 and the rank of the array returns the array itself.

Now consider sub, which returns a specific array, composed of the elements that would start with given subscripts:

(ao:sub *b*0) ; => #2A((0 1) (2 3))
(ao:sub *b*01) ; => #(2 3)
(ao:sub *b*010) ; => 2

There is also a (setf sub) function.

partition returns a consecutive chunk of an array separated along its first subscript:

(ao:partition #2A((01)
(23)
(45)
(67)
(89))
13) ; => #2A((2 3) (4 5))

and also has a (setf partition) pair.

combine is the opposite of split:

(ao:combine #(#(01) #(23))) ; => #2A((0 1) (2 3))

subvec returns a displaced subvector:

(ao:subvec #(01234) 24) ; => #(2 3)

There is also a (setf subvec) function, which is like (setf subseq) except for demanding matching lengths.

Finally, reshape can be used to displace arrays into a different shape:

(ao:reshape *a*'(32)) ; => #2A((1 2) (3 4) (5 6))

You can use t for one of the dimensions, to be filled in automatically:

(ao:reshape *b*'(1t)) ; => #2A((0 1 2 3 4 5 6 7))

reshape-col and reshape-row reshape your array into a column or row matrix, respectively.

Dimension specifications

Functions in the library accept the following in place of dimensions:

  • a list of dimensions (as for make-array),
  • a positive integer, which is used as a single-element list,
  • another array, the dimensions of which are used.

The last one allows you to specify dimensions with other arrays. For example, to reshape an array a1 to look like a2, you can use

(ao:reshape a1 a2)

instead of the longer form

(ao:reshape a1 (ao:dims a2))

Array creation and transformations

When the resulting element type cannot be inferred, functions that create and transform arrays are provided in pairs: one of these will allow you to specify the array-element-type of the result, while the other assumes it is t. The former ends with a *, and the element-type is always its first argument. I give examples for the versions without *, use the other when you are optimizing your code and you are sure you can constrain to a given element-type.

Element traversal order of these functions is unspecified. The reason for this is that the library may use parallel code in the future, so it is unsafe to rely on a particular element traversal order.

generate (and generate*) allow you to generate arrays using functions.

(ao:generate (lambda () (random10)) 3) ; => #(6 9 5)
(ao:generate #'identity'(23) :position) ; => #2A((0 1 2) (3 4 5))
(ao:generate #'identity'(22) :subscripts)
;; => #2A(((0 0) (0 1)) ((1 0) (1 1)))
(ao:generate #'cons'(22) :position-and-subscripts)
;; => #2A(((0 0 0) (1 0 1)) ((2 1 0) (3 1 1)))

Depending on the last argument, the function will be called with the (row-major) position, the subscripts, both, or no argument.

permute can permutate subscripts (you can also invert, complement, and complete permutations, look at the docstring and the unit tests). Transposing is a special case of permute:

(ao:permute '(01) *a*) ; => #2A((1 2 3) (4 5 6))

each applies a function to its (array) arguments elementwise:

(ao:each #'+#(012) #(235)) ; => #(2 4 7)

The semantics of margin are more difficult to explain, so perhaps an example will be more useful. Suppose that you want to calculate column sums in a matrix. You could permute (transpose) the matrix, split its subarrays at rank one (so you get a vector for each row), and apply the function that calculates the sum. margin automates that for you:

(ao:margin (lambda (column)
(reduce#'+ column))
#2A((01)
(23)
(57)) 0) ; => #(7 11)

But the function is much more general than this: the arguments inner and outer allow arbitrary permutations before splitting.

Finally, recycle allows you to recycle arrays along inner and outer dimensions:

(ao:recycle #(23) :inner2:outer4)
; => #3A(((2 2) (3 3)) ((2 2) (3 3)) ((2 2) (3 3)) ((2 2) (3 3)))

Scalars as 0-dimensional arrays

Library functions treat non-array objects as if they were equivalent to 0-dimensional arrays: for example, (ao:split array (rank array)) returns an array that effectively equivalent (eq) to array. Another example is recycle:

(ao:recycle 4:inner'(22)) ; => #2A((4 4) (4 4))

Stacking

You can also stack compatible arrays along any axis:

(defparameter*a1*#(012))
(defparameter*a2*#(357))
(ao:stack 0*a1**a2*) ; => #(0 1 2 3 5 7)
(ao:stack 1
(ao:reshape-col *a1*)
(ao:reshape-col *a2*)) ; => #2A((0 3) (1 5) (2 7))

Shared structure

Rules for that aren't finalized yet, see the source. Suggestions are welcome.

To-do list

benchmark and optimize walk-subscripts and walk-subscripts-list

  • instead of allocating a new list each time, could map into a preallocated one

About

Common Lisp library that facilitates working with Common Lisp arrays.

Resources

Stars

37 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' GitHub - tpapp/array-operations: Common Lisp library that facilitates working with Common Lisp arrays. · GitHub
Skip to content
This repository was archived by the owner on Feb 25, 2023. It is now read-only.

Latest commit

History

67 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Project Status: Abandoned – Initial development has started, but there has not yet been a stable, usable release; the project has been abandoned and the author(s) do not intend on continuing development.

This library is abandonned by the original author. A fork is maintained at https://github.com/Lisp-Stat/array-operations.

NOTE 2019-05-16

@bendudson and others have continued maintaining a fork of this library at https://github.com/bendudson/array-operations.

!!! important This is an alpha release. All the code works and unit tests are expected to run perfectly, but the operations are not optimized and the API change.

Introduction

array-operations is a Common Lisp library that facilitates working with Common Lisp arrays using syntax and semantics that work well with the rest of the language.

The library previously available under this name is deprecated, but you can find it here.

A quick tour of the library

Shorthand for frequently used Common Lisp array functions


The library defines the following short function names that are synomyms for Common Lisp operations:

array-operationsCommon Lisp
sizearray-total-size
rankarray-rank
dimarray-dimension
dimsarray-dimensions
nrownumber of rows in matrix
ncolnumber of columns in matrix

The array-operations package has the nickname ao, so you can use, for example, (ao:size my-array) without use'ing the package.

Displaced arrays for fun and profit

displaced array n. an array which has no storage of its own, but which is instead indirected to the storage of another array, called its target, at a specified offset, in such a way that any attempt to access the displaced array implicitly references the target array. (CLHS Glossary)

Displaced arrays are one of the niftiest features of Common Lisp. When an array is displaced to another array, it shares structure with (part of) that array. The two arrays do not need to have the same dimensions, in fact, the dimensions do not be related at all as long as the displaced array fits inside the original one. The row-major index of the former in the latter is called the offset of the displacement.

Displaced arrays are usually constructed using make-array, but this library also provides displace for that purpose:

(defparameter*a*#2A((123) (456)))
(ao:displace *a*21) ; => #(2 3)

flatten displaces to a row-major array:

(ao:flatten *a*) ; => #(1 2 3 4 5 6)

The real fun starts with split, which splits off subarrays nested within a given axis:

(ao:split *a*1) ; => #(#(1 2 3) #(4 5 6))
(defparameter*b*#3A(((01) (23))
((45) (67))))
(ao:split *b*0) ; => #3A(((0 1) (2 3)) ((4 5) (6 7)))
(ao:split *b*1) ; => #(#2A((0 1) (2 3)) #2A((4 5) (6 7)))
(ao:split *b*2) ; => #2A((#(0 1) #(2 3)) (#(4 5) #(6 7)))
(ao:split *b*3) ; => #3A(((0 1) (2 3)) ((4 5) (6 7)))

Note how splitting at 0 and the rank of the array returns the array itself.

Now consider sub, which returns a specific array, composed of the elements that would start with given subscripts:

(ao:sub *b*0) ; => #2A((0 1) (2 3))
(ao:sub *b*01) ; => #(2 3)
(ao:sub *b*010) ; => 2

There is also a (setf sub) function.

partition returns a consecutive chunk of an array separated along its first subscript:

(ao:partition #2A((01)
(23)
(45)
(67)
(89))
13) ; => #2A((2 3) (4 5))

and also has a (setf partition) pair.

combine is the opposite of split:

(ao:combine #(#(01) #(23))) ; => #2A((0 1) (2 3))

subvec returns a displaced subvector:

(ao:subvec #(01234) 24) ; => #(2 3)

There is also a (setf subvec) function, which is like (setf subseq) except for demanding matching lengths.

Finally, reshape can be used to displace arrays into a different shape:

(ao:reshape *a*'(32)) ; => #2A((1 2) (3 4) (5 6))

You can use t for one of the dimensions, to be filled in automatically:

(ao:reshape *b*'(1t)) ; => #2A((0 1 2 3 4 5 6 7))

reshape-col and reshape-row reshape your array into a column or row matrix, respectively.

Dimension specifications

Functions in the library accept the following in place of dimensions:

  • a list of dimensions (as for make-array),
  • a positive integer, which is used as a single-element list,
  • another array, the dimensions of which are used.

The last one allows you to specify dimensions with other arrays. For example, to reshape an array a1 to look like a2, you can use

(ao:reshape a1 a2)

instead of the longer form

(ao:reshape a1 (ao:dims a2))

Array creation and transformations

When the resulting element type cannot be inferred, functions that create and transform arrays are provided in pairs: one of these will allow you to specify the array-element-type of the result, while the other assumes it is t. The former ends with a *, and the element-type is always its first argument. I give examples for the versions without *, use the other when you are optimizing your code and you are sure you can constrain to a given element-type.

Element traversal order of these functions is unspecified. The reason for this is that the library may use parallel code in the future, so it is unsafe to rely on a particular element traversal order.

generate (and generate*) allow you to generate arrays using functions.

(ao:generate (lambda () (random10)) 3) ; => #(6 9 5)
(ao:generate #'identity'(23) :position) ; => #2A((0 1 2) (3 4 5))
(ao:generate #'identity'(22) :subscripts)
;; => #2A(((0 0) (0 1)) ((1 0) (1 1)))
(ao:generate #'cons'(22) :position-and-subscripts)
;; => #2A(((0 0 0) (1 0 1)) ((2 1 0) (3 1 1)))

Depending on the last argument, the function will be called with the (row-major) position, the subscripts, both, or no argument.

permute can permutate subscripts (you can also invert, complement, and complete permutations, look at the docstring and the unit tests). Transposing is a special case of permute:

(ao:permute '(01) *a*) ; => #2A((1 2 3) (4 5 6))

each applies a function to its (array) arguments elementwise:

(ao:each #'+#(012) #(235)) ; => #(2 4 7)

The semantics of margin are more difficult to explain, so perhaps an example will be more useful. Suppose that you want to calculate column sums in a matrix. You could permute (transpose) the matrix, split its subarrays at rank one (so you get a vector for each row), and apply the function that calculates the sum. margin automates that for you:

(ao:margin (lambda (column)
(reduce#'+ column))
#2A((01)
(23)
(57)) 0) ; => #(7 11)

But the function is much more general than this: the arguments inner and outer allow arbitrary permutations before splitting.

Finally, recycle allows you to recycle arrays along inner and outer dimensions:

(ao:recycle #(23) :inner2:outer4)
; => #3A(((2 2) (3 3)) ((2 2) (3 3)) ((2 2) (3 3)) ((2 2) (3 3)))

Scalars as 0-dimensional arrays

Library functions treat non-array objects as if they were equivalent to 0-dimensional arrays: for example, (ao:split array (rank array)) returns an array that effectively equivalent (eq) to array. Another example is recycle:

(ao:recycle 4:inner'(22)) ; => #2A((4 4) (4 4))

Stacking

You can also stack compatible arrays along any axis:

(defparameter*a1*#(012))
(defparameter*a2*#(357))
(ao:stack 0*a1**a2*) ; => #(0 1 2 3 5 7)
(ao:stack 1
(ao:reshape-col *a1*)
(ao:reshape-col *a2*)) ; => #2A((0 3) (1 5) (2 7))

Shared structure

Rules for that aren't finalized yet, see the source. Suggestions are welcome.

To-do list

benchmark and optimize walk-subscripts and walk-subscripts-list

  • instead of allocating a new list each time, could map into a preallocated one

About

Common Lisp library that facilitates working with Common Lisp arrays.

Resources

Stars

37 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - tpapp/array-operations: Common Lisp library that facilitates working with Common Lisp arrays. · GitHub
Skip to content
This repository was archived by the owner on Feb 25, 2023. It is now read-only.

Latest commit

History

67 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Project Status: Abandoned – Initial development has started, but there has not yet been a stable, usable release; the project has been abandoned and the author(s) do not intend on continuing development.

This library is abandonned by the original author. A fork is maintained at https://github.com/Lisp-Stat/array-operations.

NOTE 2019-05-16

@bendudson and others have continued maintaining a fork of this library at https://github.com/bendudson/array-operations.

!!! important This is an alpha release. All the code works and unit tests are expected to run perfectly, but the operations are not optimized and the API change.

Introduction

array-operations is a Common Lisp library that facilitates working with Common Lisp arrays using syntax and semantics that work well with the rest of the language.

The library previously available under this name is deprecated, but you can find it here.

A quick tour of the library

Shorthand for frequently used Common Lisp array functions


The library defines the following short function names that are synomyms for Common Lisp operations:

array-operationsCommon Lisp
sizearray-total-size
rankarray-rank
dimarray-dimension
dimsarray-dimensions
nrownumber of rows in matrix
ncolnumber of columns in matrix

The array-operations package has the nickname ao, so you can use, for example, (ao:size my-array) without use'ing the package.

Displaced arrays for fun and profit

displaced array n. an array which has no storage of its own, but which is instead indirected to the storage of another array, called its target, at a specified offset, in such a way that any attempt to access the displaced array implicitly references the target array. (CLHS Glossary)

Displaced arrays are one of the niftiest features of Common Lisp. When an array is displaced to another array, it shares structure with (part of) that array. The two arrays do not need to have the same dimensions, in fact, the dimensions do not be related at all as long as the displaced array fits inside the original one. The row-major index of the former in the latter is called the offset of the displacement.

Displaced arrays are usually constructed using make-array, but this library also provides displace for that purpose:

(defparameter*a*#2A((123) (456)))
(ao:displace *a*21) ; => #(2 3)

flatten displaces to a row-major array:

(ao:flatten *a*) ; => #(1 2 3 4 5 6)

The real fun starts with split, which splits off subarrays nested within a given axis:

(ao:split *a*1) ; => #(#(1 2 3) #(4 5 6))
(defparameter*b*#3A(((01) (23))
((45) (67))))
(ao:split *b*0) ; => #3A(((0 1) (2 3)) ((4 5) (6 7)))
(ao:split *b*1) ; => #(#2A((0 1) (2 3)) #2A((4 5) (6 7)))
(ao:split *b*2) ; => #2A((#(0 1) #(2 3)) (#(4 5) #(6 7)))
(ao:split *b*3) ; => #3A(((0 1) (2 3)) ((4 5) (6 7)))

Note how splitting at 0 and the rank of the array returns the array itself.

Now consider sub, which returns a specific array, composed of the elements that would start with given subscripts:

(ao:sub *b*0) ; => #2A((0 1) (2 3))
(ao:sub *b*01) ; => #(2 3)
(ao:sub *b*010) ; => 2

There is also a (setf sub) function.

partition returns a consecutive chunk of an array separated along its first subscript:

(ao:partition #2A((01)
(23)
(45)
(67)
(89))
13) ; => #2A((2 3) (4 5))

and also has a (setf partition) pair.

combine is the opposite of split:

(ao:combine #(#(01) #(23))) ; => #2A((0 1) (2 3))

subvec returns a displaced subvector:

(ao:subvec #(01234) 24) ; => #(2 3)

There is also a (setf subvec) function, which is like (setf subseq) except for demanding matching lengths.

Finally, reshape can be used to displace arrays into a different shape:

(ao:reshape *a*'(32)) ; => #2A((1 2) (3 4) (5 6))

You can use t for one of the dimensions, to be filled in automatically:

(ao:reshape *b*'(1t)) ; => #2A((0 1 2 3 4 5 6 7))

reshape-col and reshape-row reshape your array into a column or row matrix, respectively.

Dimension specifications

Functions in the library accept the following in place of dimensions:

  • a list of dimensions (as for make-array),
  • a positive integer, which is used as a single-element list,
  • another array, the dimensions of which are used.

The last one allows you to specify dimensions with other arrays. For example, to reshape an array a1 to look like a2, you can use

(ao:reshape a1 a2)

instead of the longer form

(ao:reshape a1 (ao:dims a2))

Array creation and transformations

When the resulting element type cannot be inferred, functions that create and transform arrays are provided in pairs: one of these will allow you to specify the array-element-type of the result, while the other assumes it is t. The former ends with a *, and the element-type is always its first argument. I give examples for the versions without *, use the other when you are optimizing your code and you are sure you can constrain to a given element-type.

Element traversal order of these functions is unspecified. The reason for this is that the library may use parallel code in the future, so it is unsafe to rely on a particular element traversal order.

generate (and generate*) allow you to generate arrays using functions.

(ao:generate (lambda () (random10)) 3) ; => #(6 9 5)
(ao:generate #'identity'(23) :position) ; => #2A((0 1 2) (3 4 5))
(ao:generate #'identity'(22) :subscripts)
;; => #2A(((0 0) (0 1)) ((1 0) (1 1)))
(ao:generate #'cons'(22) :position-and-subscripts)
;; => #2A(((0 0 0) (1 0 1)) ((2 1 0) (3 1 1)))

Depending on the last argument, the function will be called with the (row-major) position, the subscripts, both, or no argument.

permute can permutate subscripts (you can also invert, complement, and complete permutations, look at the docstring and the unit tests). Transposing is a special case of permute:

(ao:permute '(01) *a*) ; => #2A((1 2 3) (4 5 6))

each applies a function to its (array) arguments elementwise:

(ao:each #'+#(012) #(235)) ; => #(2 4 7)

The semantics of margin are more difficult to explain, so perhaps an example will be more useful. Suppose that you want to calculate column sums in a matrix. You could permute (transpose) the matrix, split its subarrays at rank one (so you get a vector for each row), and apply the function that calculates the sum. margin automates that for you:

(ao:margin (lambda (column)
(reduce#'+ column))
#2A((01)
(23)
(57)) 0) ; => #(7 11)

But the function is much more general than this: the arguments inner and outer allow arbitrary permutations before splitting.

Finally, recycle allows you to recycle arrays along inner and outer dimensions:

(ao:recycle #(23) :inner2:outer4)
; => #3A(((2 2) (3 3)) ((2 2) (3 3)) ((2 2) (3 3)) ((2 2) (3 3)))

Scalars as 0-dimensional arrays

Library functions treat non-array objects as if they were equivalent to 0-dimensional arrays: for example, (ao:split array (rank array)) returns an array that effectively equivalent (eq) to array. Another example is recycle:

(ao:recycle 4:inner'(22)) ; => #2A((4 4) (4 4))

Stacking

You can also stack compatible arrays along any axis:

(defparameter*a1*#(012))
(defparameter*a2*#(357))
(ao:stack 0*a1**a2*) ; => #(0 1 2 3 5 7)
(ao:stack 1
(ao:reshape-col *a1*)
(ao:reshape-col *a2*)) ; => #2A((0 3) (1 5) (2 7))

Shared structure

Rules for that aren't finalized yet, see the source. Suggestions are welcome.

To-do list

benchmark and optimize walk-subscripts and walk-subscripts-list

  • instead of allocating a new list each time, could map into a preallocated one

About

Common Lisp library that facilitates working with Common Lisp arrays.

Resources

Stars

37 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - tpapp/array-operations: Common Lisp library that facilitates working with Common Lisp arrays. · GitHub
Skip to content
This repository was archived by the owner on Feb 25, 2023. It is now read-only.

Latest commit

History

67 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Project Status: Abandoned – Initial development has started, but there has not yet been a stable, usable release; the project has been abandoned and the author(s) do not intend on continuing development.

This library is abandonned by the original author. A fork is maintained at https://github.com/Lisp-Stat/array-operations.

NOTE 2019-05-16

@bendudson and others have continued maintaining a fork of this library at https://github.com/bendudson/array-operations.

!!! important This is an alpha release. All the code works and unit tests are expected to run perfectly, but the operations are not optimized and the API change.

Introduction

array-operations is a Common Lisp library that facilitates working with Common Lisp arrays using syntax and semantics that work well with the rest of the language.

The library previously available under this name is deprecated, but you can find it here.

A quick tour of the library

Shorthand for frequently used Common Lisp array functions


The library defines the following short function names that are synomyms for Common Lisp operations:

array-operationsCommon Lisp
sizearray-total-size
rankarray-rank
dimarray-dimension
dimsarray-dimensions
nrownumber of rows in matrix
ncolnumber of columns in matrix

The array-operations package has the nickname ao, so you can use, for example, (ao:size my-array) without use'ing the package.

Displaced arrays for fun and profit

displaced array n. an array which has no storage of its own, but which is instead indirected to the storage of another array, called its target, at a specified offset, in such a way that any attempt to access the displaced array implicitly references the target array. (CLHS Glossary)

Displaced arrays are one of the niftiest features of Common Lisp. When an array is displaced to another array, it shares structure with (part of) that array. The two arrays do not need to have the same dimensions, in fact, the dimensions do not be related at all as long as the displaced array fits inside the original one. The row-major index of the former in the latter is called the offset of the displacement.

Displaced arrays are usually constructed using make-array, but this library also provides displace for that purpose:

(defparameter*a*#2A((123) (456)))
(ao:displace *a*21) ; => #(2 3)

flatten displaces to a row-major array:

(ao:flatten *a*) ; => #(1 2 3 4 5 6)

The real fun starts with split, which splits off subarrays nested within a given axis:

(ao:split *a*1) ; => #(#(1 2 3) #(4 5 6))
(defparameter*b*#3A(((01) (23))
((45) (67))))
(ao:split *b*0) ; => #3A(((0 1) (2 3)) ((4 5) (6 7)))
(ao:split *b*1) ; => #(#2A((0 1) (2 3)) #2A((4 5) (6 7)))
(ao:split *b*2) ; => #2A((#(0 1) #(2 3)) (#(4 5) #(6 7)))
(ao:split *b*3) ; => #3A(((0 1) (2 3)) ((4 5) (6 7)))

Note how splitting at 0 and the rank of the array returns the array itself.

Now consider sub, which returns a specific array, composed of the elements that would start with given subscripts:

(ao:sub *b*0) ; => #2A((0 1) (2 3))
(ao:sub *b*01) ; => #(2 3)
(ao:sub *b*010) ; => 2

There is also a (setf sub) function.

partition returns a consecutive chunk of an array separated along its first subscript:

(ao:partition #2A((01)
(23)
(45)
(67)
(89))
13) ; => #2A((2 3) (4 5))

and also has a (setf partition) pair.

combine is the opposite of split:

(ao:combine #(#(01) #(23))) ; => #2A((0 1) (2 3))

subvec returns a displaced subvector:

(ao:subvec #(01234) 24) ; => #(2 3)

There is also a (setf subvec) function, which is like (setf subseq) except for demanding matching lengths.

Finally, reshape can be used to displace arrays into a different shape:

(ao:reshape *a*'(32)) ; => #2A((1 2) (3 4) (5 6))

You can use t for one of the dimensions, to be filled in automatically:

(ao:reshape *b*'(1t)) ; => #2A((0 1 2 3 4 5 6 7))

reshape-col and reshape-row reshape your array into a column or row matrix, respectively.

Dimension specifications

Functions in the library accept the following in place of dimensions:

  • a list of dimensions (as for make-array),
  • a positive integer, which is used as a single-element list,
  • another array, the dimensions of which are used.

The last one allows you to specify dimensions with other arrays. For example, to reshape an array a1 to look like a2, you can use

(ao:reshape a1 a2)

instead of the longer form

(ao:reshape a1 (ao:dims a2))

Array creation and transformations

When the resulting element type cannot be inferred, functions that create and transform arrays are provided in pairs: one of these will allow you to specify the array-element-type of the result, while the other assumes it is t. The former ends with a *, and the element-type is always its first argument. I give examples for the versions without *, use the other when you are optimizing your code and you are sure you can constrain to a given element-type.

Element traversal order of these functions is unspecified. The reason for this is that the library may use parallel code in the future, so it is unsafe to rely on a particular element traversal order.

generate (and generate*) allow you to generate arrays using functions.

(ao:generate (lambda () (random10)) 3) ; => #(6 9 5)
(ao:generate #'identity'(23) :position) ; => #2A((0 1 2) (3 4 5))
(ao:generate #'identity'(22) :subscripts)
;; => #2A(((0 0) (0 1)) ((1 0) (1 1)))
(ao:generate #'cons'(22) :position-and-subscripts)
;; => #2A(((0 0 0) (1 0 1)) ((2 1 0) (3 1 1)))

Depending on the last argument, the function will be called with the (row-major) position, the subscripts, both, or no argument.

permute can permutate subscripts (you can also invert, complement, and complete permutations, look at the docstring and the unit tests). Transposing is a special case of permute:

(ao:permute '(01) *a*) ; => #2A((1 2 3) (4 5 6))

each applies a function to its (array) arguments elementwise:

(ao:each #'+#(012) #(235)) ; => #(2 4 7)

The semantics of margin are more difficult to explain, so perhaps an example will be more useful. Suppose that you want to calculate column sums in a matrix. You could permute (transpose) the matrix, split its subarrays at rank one (so you get a vector for each row), and apply the function that calculates the sum. margin automates that for you:

(ao:margin (lambda (column)
(reduce#'+ column))
#2A((01)
(23)
(57)) 0) ; => #(7 11)

But the function is much more general than this: the arguments inner and outer allow arbitrary permutations before splitting.

Finally, recycle allows you to recycle arrays along inner and outer dimensions:

(ao:recycle #(23) :inner2:outer4)
; => #3A(((2 2) (3 3)) ((2 2) (3 3)) ((2 2) (3 3)) ((2 2) (3 3)))

Scalars as 0-dimensional arrays

Library functions treat non-array objects as if they were equivalent to 0-dimensional arrays: for example, (ao:split array (rank array)) returns an array that effectively equivalent (eq) to array. Another example is recycle:

(ao:recycle 4:inner'(22)) ; => #2A((4 4) (4 4))

Stacking

You can also stack compatible arrays along any axis:

(defparameter*a1*#(012))
(defparameter*a2*#(357))
(ao:stack 0*a1**a2*) ; => #(0 1 2 3 5 7)
(ao:stack 1
(ao:reshape-col *a1*)
(ao:reshape-col *a2*)) ; => #2A((0 3) (1 5) (2 7))

Shared structure

Rules for that aren't finalized yet, see the source. Suggestions are welcome.

To-do list

benchmark and optimize walk-subscripts and walk-subscripts-list

  • instead of allocating a new list each time, could map into a preallocated one

About

Common Lisp library that facilitates working with Common Lisp arrays.

Resources

Stars

37 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); GitHub - tpapp/array-operations: Common Lisp library that facilitates working with Common Lisp arrays. · GitHub
Skip to content
This repository was archived by the owner on Feb 25, 2023. It is now read-only.

Latest commit

History

67 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Project Status: Abandoned – Initial development has started, but there has not yet been a stable, usable release; the project has been abandoned and the author(s) do not intend on continuing development.

This library is abandonned by the original author. A fork is maintained at https://github.com/Lisp-Stat/array-operations.

NOTE 2019-05-16

@bendudson and others have continued maintaining a fork of this library at https://github.com/bendudson/array-operations.

!!! important This is an alpha release. All the code works and unit tests are expected to run perfectly, but the operations are not optimized and the API change.

Introduction

array-operations is a Common Lisp library that facilitates working with Common Lisp arrays using syntax and semantics that work well with the rest of the language.

The library previously available under this name is deprecated, but you can find it here.

A quick tour of the library

Shorthand for frequently used Common Lisp array functions


The library defines the following short function names that are synomyms for Common Lisp operations:

array-operationsCommon Lisp
sizearray-total-size
rankarray-rank
dimarray-dimension
dimsarray-dimensions
nrownumber of rows in matrix
ncolnumber of columns in matrix

The array-operations package has the nickname ao, so you can use, for example, (ao:size my-array) without use'ing the package.

Displaced arrays for fun and profit

displaced array n. an array which has no storage of its own, but which is instead indirected to the storage of another array, called its target, at a specified offset, in such a way that any attempt to access the displaced array implicitly references the target array. (CLHS Glossary)

Displaced arrays are one of the niftiest features of Common Lisp. When an array is displaced to another array, it shares structure with (part of) that array. The two arrays do not need to have the same dimensions, in fact, the dimensions do not be related at all as long as the displaced array fits inside the original one. The row-major index of the former in the latter is called the offset of the displacement.

Displaced arrays are usually constructed using make-array, but this library also provides displace for that purpose:

(defparameter*a*#2A((123) (456)))
(ao:displace *a*21) ; => #(2 3)

flatten displaces to a row-major array:

(ao:flatten *a*) ; => #(1 2 3 4 5 6)

The real fun starts with split, which splits off subarrays nested within a given axis:

(ao:split *a*1) ; => #(#(1 2 3) #(4 5 6))
(defparameter*b*#3A(((01) (23))
((45) (67))))
(ao:split *b*0) ; => #3A(((0 1) (2 3)) ((4 5) (6 7)))
(ao:split *b*1) ; => #(#2A((0 1) (2 3)) #2A((4 5) (6 7)))
(ao:split *b*2) ; => #2A((#(0 1) #(2 3)) (#(4 5) #(6 7)))
(ao:split *b*3) ; => #3A(((0 1) (2 3)) ((4 5) (6 7)))

Note how splitting at 0 and the rank of the array returns the array itself.

Now consider sub, which returns a specific array, composed of the elements that would start with given subscripts:

(ao:sub *b*0) ; => #2A((0 1) (2 3))
(ao:sub *b*01) ; => #(2 3)
(ao:sub *b*010) ; => 2

There is also a (setf sub) function.

partition returns a consecutive chunk of an array separated along its first subscript:

(ao:partition #2A((01)
(23)
(45)
(67)
(89))
13) ; => #2A((2 3) (4 5))

and also has a (setf partition) pair.

combine is the opposite of split:

(ao:combine #(#(01) #(23))) ; => #2A((0 1) (2 3))

subvec returns a displaced subvector:

(ao:subvec #(01234) 24) ; => #(2 3)

There is also a (setf subvec) function, which is like (setf subseq) except for demanding matching lengths.

Finally, reshape can be used to displace arrays into a different shape:

(ao:reshape *a*'(32)) ; => #2A((1 2) (3 4) (5 6))

You can use t for one of the dimensions, to be filled in automatically:

(ao:reshape *b*'(1t)) ; => #2A((0 1 2 3 4 5 6 7))

reshape-col and reshape-row reshape your array into a column or row matrix, respectively.

Dimension specifications

Functions in the library accept the following in place of dimensions:

  • a list of dimensions (as for make-array),
  • a positive integer, which is used as a single-element list,
  • another array, the dimensions of which are used.

The last one allows you to specify dimensions with other arrays. For example, to reshape an array a1 to look like a2, you can use

(ao:reshape a1 a2)

instead of the longer form

(ao:reshape a1 (ao:dims a2))

Array creation and transformations

When the resulting element type cannot be inferred, functions that create and transform arrays are provided in pairs: one of these will allow you to specify the array-element-type of the result, while the other assumes it is t. The former ends with a *, and the element-type is always its first argument. I give examples for the versions without *, use the other when you are optimizing your code and you are sure you can constrain to a given element-type.

Element traversal order of these functions is unspecified. The reason for this is that the library may use parallel code in the future, so it is unsafe to rely on a particular element traversal order.

generate (and generate*) allow you to generate arrays using functions.

(ao:generate (lambda () (random10)) 3) ; => #(6 9 5)
(ao:generate #'identity'(23) :position) ; => #2A((0 1 2) (3 4 5))
(ao:generate #'identity'(22) :subscripts)
;; => #2A(((0 0) (0 1)) ((1 0) (1 1)))
(ao:generate #'cons'(22) :position-and-subscripts)
;; => #2A(((0 0 0) (1 0 1)) ((2 1 0) (3 1 1)))

Depending on the last argument, the function will be called with the (row-major) position, the subscripts, both, or no argument.

permute can permutate subscripts (you can also invert, complement, and complete permutations, look at the docstring and the unit tests). Transposing is a special case of permute:

(ao:permute '(01) *a*) ; => #2A((1 2 3) (4 5 6))

each applies a function to its (array) arguments elementwise:

(ao:each #'+#(012) #(235)) ; => #(2 4 7)

The semantics of margin are more difficult to explain, so perhaps an example will be more useful. Suppose that you want to calculate column sums in a matrix. You could permute (transpose) the matrix, split its subarrays at rank one (so you get a vector for each row), and apply the function that calculates the sum. margin automates that for you:

(ao:margin (lambda (column)
(reduce#'+ column))
#2A((01)
(23)
(57)) 0) ; => #(7 11)

But the function is much more general than this: the arguments inner and outer allow arbitrary permutations before splitting.

Finally, recycle allows you to recycle arrays along inner and outer dimensions:

(ao:recycle #(23) :inner2:outer4)
; => #3A(((2 2) (3 3)) ((2 2) (3 3)) ((2 2) (3 3)) ((2 2) (3 3)))

Scalars as 0-dimensional arrays

Library functions treat non-array objects as if they were equivalent to 0-dimensional arrays: for example, (ao:split array (rank array)) returns an array that effectively equivalent (eq) to array. Another example is recycle:

(ao:recycle 4:inner'(22)) ; => #2A((4 4) (4 4))

Stacking

You can also stack compatible arrays along any axis:

(defparameter*a1*#(012))
(defparameter*a2*#(357))
(ao:stack 0*a1**a2*) ; => #(0 1 2 3 5 7)
(ao:stack 1
(ao:reshape-col *a1*)
(ao:reshape-col *a2*)) ; => #2A((0 3) (1 5) (2 7))

Shared structure

Rules for that aren't finalized yet, see the source. Suggestions are welcome.

To-do list

benchmark and optimize walk-subscripts and walk-subscripts-list

  • instead of allocating a new list each time, could map into a preallocated one

About

Common Lisp library that facilitates working with Common Lisp arrays.

Resources

Stars

37 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages