How to assign an initialized array to an array of arrays in TwinCAT - plc

I'm trying to assign two initialized arrays evenNumbers and oddNumbers to an array of arrays integers:
PROGRAM ArrayInit
VAR
evenNumbers : ARRAY[1..3] OF INT := [2, 4, 6];
oddNumbers: ARRAY[1..3] OF INT := [1, 3, 5];
integers : ARRAY[1..2] OF ARRAY[1..3] OF INT := [evenNumbers, oddNumbers];
END_VAR
This code gives me a compiler error
Array initialisation expected
Of course I can directly initialize integers with the numbers that I want like so:
PROGRAM ArrayInit
VAR
integers: ARRAY[1..2] OF ARRAY[1..3] OF INT := [
[2, 4, 6], [1, 3, 5]
];
END_VAR
or like Sergey mentioned
PROGRAM ArrayInit
VAR
integers: ARRAY[1..2, 1..3] OF INT := [
2, 4, 6, 1, 3, 5
];
END_VAR
However, if the original arrays are very big and/or I want to document what these different arrays are, a descriptive name would be nice. I.e. integers : ARRAY[1..2] OF ARRAY[1..3] OF INT := [evenNumbers, oddNumbers]; nicely shows that the integers has two lists, one with even and one with odd numbers.
I've also tried to initialize integers as integers: ARRAY[1..2] OF ARRAY[1..3] OF INT := [[evenNumbers], [oddNumbers]];, but this gives me the compiler error:
Cannot convert type 'ARRAY [1..3] OF INT' to type 'INT'
Now I wonder if it even possible? If so, does anyone know how I can do it?

To assign multiple level array you do it in one line.
combinedSet : ARRAY[1..2, 1..2] OF INT := [1,2,3,4];
will result in array
[
1 => [
1 => 1,
2 => 2
],
2 => [
1 => 2,
2 => 4
]
]
SO first it assign all element of first element [1, 1], [1, 2], [1, 3] ... and then nest one [2, 1], [2, 2], [2, 3] ...
Additional information
Most simple way to merge 2 arrays into one multi dimensional is:
PROGRAM PLC_PRG
VAR
arr1 : ARRAY[1..3] OF INT := [1,3,5];
arr2 : ARRAY[1..3] OF INT := [2,4,6];
combinedSet : ARRAY[1..2] OF ARRAY[1..3] OF INT;
END_VAR
combinedSet[1] := arr1;
combinedSet[2] := arr2;
END_PROGRAM
The reason this does not work
integers : ARRAY[1..2] OF ARRAY[1..3] OF INT := [evenNumbers, oddNumbers];
Because evenNumbers and oddNumbers are not initialized at the moment of use. If you would declare those in VAR CONSTANT it would probably work but then you would not be able to change content of those arrays in program.

I don't think you can join arrays while initializing a third (see the edit below). What you can do however is call a function at the start of the program once that joins those two arrays:
PROGRAM PLC_PRG
VAR
arr1: ARRAY [0..2] OF INT := [1, 2, 3];
arr2: ARRAY [0..2] OF INT := [4, 5, 6];
arr3: ARRAY [0..5] OF INT;
initialized: BOOL := FALSE;
END_VAR
IF (NOT initialized) THEN
initialized := TRUE;
JOIN_INT_ARRAYS(arr1 := arr1, arr2 := arr2, dest_arr := arr3);
END_IF
// Assume that:
// - the destination ARRAY size can fit all source ARRAYs
// - all ARRAYs store INTs
FUNCTION JOIN_INT_ARRAYS
VAR CONSTANT
size_of_int: DWORD := SIZEOF(INT);
END_VAR
VAR_IN_OUT
arr1: ARRAY [*] OF INT;
arr2: ARRAY [*] OF INT;
dest_arr: ARRAY [*] OF INT;
END_VAR
VAR
arr1_len: DWORD := DINT_TO_DWORD(UPPER_BOUND(arr1, 1) - LOWER_BOUND(arr1, 1) + 1) * size_of_int;
END_VAR
MEMUtils.MemCpy(pbySrc := arr1, pbyDest := dest_arr, dwSize := arr1_len);
MEMUtils.MemCpy(pbySrc := arr2, pbyDest := dest_arr + arr1_len, dwSize := DINT_TO_DWORD(UPPER_BOUND(arr2, 1) - LOWER_BOUND(arr2, 1) + 1) * size_of_int);
Result:
A few things to note:
I used the MemCpy function from the MEMUtils library. If you don't have it, or don't want to add it into your project, you can manually copy values from one array into another using a FOR loop.
I have omitted range checking, which can be extremely dangerous. If you want extra protection, then add it yourself.
avoid passing arr_dest as arr1 or arr2. Trying to copy from an array into itself may lead into problems.
EDIT:
Actually, this seems to work:
integers: ARRAY [0..1] OF ARRAY [0..2] OF INT := [[2, 4, 6], [1, 3, 5]];
evenNumbers: ARRAY [0..2] OF INT := integers[0];
oddNumbers: ARRAY [0..2] OF INT := integers[1];
result:
But I don't know if this is your desired outcome. If you want a continuous array as the combined array, then you can try this:
// in Program
evenNumbers: ARRAY [0..2] OF INT := [2, 4, 6];
oddNumbers: ARRAY [0..2] OF INT := [1, 3, 5];
integers: ARRAY [0..5] OF INT := JOIN_INT_ARRAYS_3_3_6(arr1 := evenNumbers, arr2 := oddNumbers);
// (left)3 + (right)3 = (result)6
FUNCTION JOIN_INT_ARRAYS_3_3_6 : ARRAY [0..5] OF INT
VAR_IN_OUT
arr1: ARRAY [0..2] OF INT;
arr2: ARRAY [0..2] OF INT;
END_VAR
VAR
i: USINT;
END_VAR
FOR i := 0 TO 2 DO
JOIN_INT_ARRAYS_3_3_6[i] := arr1[i];
END_FOR
FOR i := 0 TO 2 DO
JOIN_INT_ARRAYS_3_3_6[i + 3] := arr2[i];
END_FOR
and the result:
However, with this method the function can't be general, so you will have to modify it's input and output array sizes every time the conditions change, and create many if you want to use this in several places, thus it's not elegant, and personally I'd avoid it, but if this works for you, then here it is.
Also, this seems to give me a C0441: Access to uninitialized VAR_IN_OUT variable warning, so another reason to try to avoid it.

Your line integers : ARRAY[1..2] OF ARRAY[1..3] OF INT := [evenNumbers, oddNumbers]; works fine when evenNumbers and oddNumbers are constants. You cannot use non-constant variables to initialize other variables.
In case you need evenNumbers and oddNumbers to be changed later, you could create two constants evenNumbersConst and oddNumbersConst and use them just for the initialization like so:
VAR
evenNumbers : ARRAY[1..3] OF INT := evenNumbersConst;
oddNumbers : ARRAY[1..3] OF INT := oddNumbersConst;
integers : ARRAY[1..2] OF ARRAY[1..3] OF INT := [evenNumbersConst, oddNumbersConst];
END_VAR
VAR CONSTANT
evenNumbersConst : ARRAY[1..3] OF INT := [2, 4, 6];
oddNumbersConst : ARRAY[1..3] OF INT := [1, 3, 5];
END_VAR

Related

Can anyone help me explain why the code are different results

I have a code sample below , Can someone help me explain why the results are different
int : m=3;
int : n=2;
array[1..m,1..n] of int: va=[|1,2|
3,4|
5,6|];
array [1..m,1..n] of var int : val;
constraint forall(i in 1..m,j in 1..n )( i<2->val[i,j]=va[i,j]+1 );
constraint forall(i in 1..m,j in 1..n )( i>=2->val[i,j]=va[i,j]+3 );
output [ show(val) ];
int : m=3;
int : n=2;
array[1..m,1..n] of int: va=[|1,2|
3,4|
5,6|];
array [1..m,1..n] of var int : val;
constraint forall(i in 1..m,j in 1..n )( i>=2->val[i,j]=va[i,j]+3 /\ i<2->val[i,j]=va[i,j]+1 );
output [ show(val) ];
if i can write this logic in one constraint?
In your second model the two implications (->) and the conjunction (/\) bind different then using two constraint sections.
The following give the same result as model 1. Note the parenthesis around the implications:
int : m=3;
int : n=2;
array[1..m,1..n] of int: va=[|1,2|
3,4|
5,6|];
array [1..m,1..n] of var int : val;
constraint forall(i in 1..m,j in 1..n )( (i>=2->val[i,j]=va[i,j]+3) /\
(i<2->val[i,j]=va[i,j]+1) );
output [ show(val) ];
Now both models outputs:
[2, 3, 6, 7, 8, 9]
----------
==========

How to implement A = sparse(I, J, K) (sparse matrix from triplet) in a Fortran mex file?

I'm trying to create a sparse square matrix in Matlab through a mex function (written in Fortran). I want something like A = sparse(I,J,K) . My triplets look like this, there are repetitions among the entries
femi = [1, 2, 3, 2, 2, 4, 5, 5, 4, 6, 6, 5, 5, 2]
femj = [2, 2, 1, 1, 1, 3, 3, 6, 3, 1, 1, 2, 2, 4]
femk = [2, 1, 5, 4, 2, 4, 5, 7, 2, 1, 6, 2, 1, 4]
I've written a rough piece of code, it works for small matrix dimensions, but it's much slower than the intrinsic Matlab's sparse. Since I have almost no background in coding, I don't know what I'm doing wrong (wrong way to allocate variables? too many do loops?). Any help is appreciated. Thank you. This is the mex computational subroutine. It returns the pr, ir, jc indices array to give to the sparse matrix
subroutine new_sparse(femi, femj, femk, pr, ir, jc, n, m)
implicit none
intrinsic:: SUM, COUNT, ANY
integer :: i, j, k, n, indjc, m
real*8 :: femi(n), femj(n), femk(n)
real*8 :: pr(n)
integer :: ir(n),jc(m+1)
logical :: indices(n)
indices = .false.
k = 1
indjc = 0
jc(1) = 0
do j=1,m
do i =1,m
indices = [femi==i .and. femj==j]
if (ANY(indices .eqv. .true.)) then
ir(k) = i-1
pr(k) = SUM(femk, indices)
k = k+1
indjc = indjc + 1
end if
end do
if (indjc/=0) then
jc(j+1) = jc(j) + indjc
indjc = 0
else
jc(j+1) = jc(j)
end if
end do
return
end
Edit:
As suggested by users #jack and #veryreverie in the comments below, it's better to sort directly femi, femj and femk. I guess that ranking/sorting femi first (and sorting femj and femk according to femi) and then ranking/sorting femj (and sorting femi and femk according to femj) provides the desired result. The only thing left is to deal with duplicates.
Edit #2 :
I translated line by line the serialized version of the C code by Engblom and Lukarksi . This document explains very clearly their reasoning and I think it's useful for beginners like me. However, due to my inexperience, I was unable to translate the parallelized version of the code. Maybe that prompts another question.
subroutine new_sparse(ir, jcS, pr, MatI, MatJ, MatK, n, m)
! use omp_lib
implicit none
integer, parameter :: dp = selected_real_kind(15,300)
integer, intent(in) :: n, m
real(dp), intent(in) :: MatK(n), MatI(n), MatJ(n)
! integer*8, intent(out) :: nnew
integer :: i, k, col, row, c, r !, nthreads
integer :: hcol(m+1), jcS(m+1), jrS(m+1)
integer :: ixijs, irank(n), rank(n)
real*8 :: pr(*)
integer :: ir(*)
hcol = 0
jcS = 0
jrS = 0
do i = 1,n
jrS(MatI(i)+1) = jrS(MatI(i)+1)+1
end do
do r = 2,m+1
jrS(r) = jrS(r) + jrS(r-1)
end do
do i = 1,n
rank(jrS(MatI(i))+1) = i
jrS(MatI(i)) = jrS(MatI(i)) + 1
end do
k = 1
do row = 1,m
do i = k , jrS(row)
ixijs = rank(i)
col = MatJ(ixijs)
if (hcol(col) < row) then
hcol(col) = row
jcS(col+1) = jcS(col+1)+1
end if
irank(ixijs) = jcS(col+1)
k = k+1
end do
end do
do c = 2,m+1
jcS(c) = jcS(c) + jcS(c-1)
end do
do i = 1,n
irank(i) = irank(i) + jcS(MatJ(i))
end do
ir(irank) = MatI-1
do i = 1,n
pr(irank(i)) = pr(irank(i)) + MatK(i)
end do
return
end
This should work:
module test
implicit none
! This should probably be whatever floating point format Matlab uses.
integer, parameter :: dp = selected_real_kind(15,300)
contains
subroutine new_sparse(femi, femj, femk, pr, ir, jc, n, m)
integer, intent(in) :: n ! The size of femi, femj, femk.
integer, intent(in) :: m ! The no. of rows (and cols) in the matrix.
integer, intent(in) :: femi(n) ! The input i indices.
integer, intent(in) :: femj(n) ! The input j indices.
real(dp), intent(in) :: femk(n) ! The input values.
real(dp), intent(out) :: pr(n) ! The output values.
integer, intent(out) :: ir(n) ! The output i indices.
integer, intent(out) :: jc(m+1) ! Column j has jc(j+1)-jc(j) non-zero entries
! loop indices.
integer :: a,b
! Initialise jc.
! All elements of `jc` are `1` as the output initially contains no elements.
jc = 1
! Loop over the input elements.
do_a : do a=1,n
associate(i=>femi(a), j=>femj(a), k=>femk(a))
! Loop over the stored entries in column j of the output,
! looking for element (i,j).
do b=jc(j),jc(j+1)-1
! Element (i,j) is already in the output, update the output and cycle.
if (ir(b)==i) then
pr(b) = pr(b) + femk(a)
cycle do_a
endif
enddo
! Element (i,j) is not already in the output.
! First make room for the new element in ir and pr,
! then add the element to ir and pr,
! then update jc.
ir(jc(j+1)+1:jc(m+1)) = ir(jc(j+1):jc(m+1)-1)
pr(jc(j+1)+1:jc(m+1)) = pr(jc(j+1):jc(m+1)-1)
ir(jc(j+1)) = i
pr(jc(j+1)) = k
jc(j+1:) = jc(j+1:) + 1
end associate
enddo do_a
end subroutine
end module
program prog
use test
implicit none
integer, parameter :: n = 14
integer, parameter :: m = 6
integer :: femi(n), femj(n)
real(dp) :: femk(n)
real(dp) :: pr(n)
integer :: ir(n),jc(m+1)
integer :: a,b
femi = [1, 2, 3, 2, 2, 4, 5, 5, 4, 6, 6, 5, 5, 2]
femj = [2, 2, 1, 1, 1, 3, 3, 6, 3, 1, 1, 2, 2, 4]
femk = real([2, 1, 5, 4, 2, 4, 5, 7, 2, 1, 6, 2, 1, 4], dp)
write(*,*) 'Input:'
do a=1,n
write(*,'(a,i0,a,i0,a,f2.0)') '(',femi(a),',',femj(a),') : ',femk(a)
enddo
write(*,*)
call new_sparse(femi,femj,femk,pr,ir,jc,n,m)
write(*,*) 'Output:'
do a=1,m
do b=jc(a),jc(a+1)-1
write(*,'(a,i0,a,i0,a,f2.0)') '(',ir(b),',',a,') : ',pr(b)
enddo
enddo
end program
This writes:
Input:
(1,2) : 2.
(2,2) : 1.
(3,1) : 5.
(2,1) : 4.
(2,1) : 2.
(4,3) : 4.
(5,3) : 5.
(5,6) : 7.
(4,3) : 2.
(6,1) : 1.
(6,1) : 6.
(5,2) : 2.
(5,2) : 1.
(2,4) : 4.
Output:
(3,1) : 5.
(2,1) : 6.
(6,1) : 7.
(1,2) : 2.
(2,2) : 1.
(5,2) : 3.
(4,3) : 6.
(5,3) : 5.
(2,4) : 4.
(5,6) : 7.
The bottleneck in your algorithm comes from the instructions indices = [femi==i .and. femj==j], any(indices .eqv. .true.) and sum(femk, indices). These all take O(n) operations, and as these are within a double loop the overall cost of the subroutine is O(m^2*n).
My algorithm works in two stages. The first stage, the do b=jc(j),jc(j+1)-1 loop, compares each element in the input with each element in the matching column of the output, for a maximum cost of O(mn) operations. If the input element is found in the output, then the value is updated and nothing more needs to be done.
If the input element is not found in the output, then it needs to be added to the output. This is handled by the second stage, the code after the do b... loop. Since this needs to move the output elements in order to make space for the new element, this stage has a maximum of O(n'^2) operations, where n' is the number of unique elements in the input, which should satisfy n'<=n and n'<<m^2 for a sparse matrix.
My algorithm should run a lot faster for large m and n, but it certainly has a lot of scope for improvement. I suspect it is worth using an intermediate data structure for storing ir and pr, so that new elements can be inserted without having to re-arrange all the elements to do so.

How do I use value of variable in Maple

How do I get Maple to give the value of a variable in the RHS of an expression, rather than treat it as a variable in an expression. In the following code I want a list of three functions of x which are quadratics with different offsets, but it's not what I get:
ix := [-1,0,1]:
b := []:
for a in ix
do
b := [op(b),x->(x-a)^2];
end do;
and the output is
while I would like it to be (for the last line)
b := [ x -> (x+1)^2, x -> x^2, x -> (x-1)^2 ]
Your problem is that you are trying to use values for a in the construction of the body of a procedure, and not the "RHS of an expression" as you stated.
Try not to use the term "function" in this context, as it just muddles the distinction between expression and procedure.
Here is a way to get the values from ix as replacements for a in a generated list of procedures with (x-a)^2 in the body.
restart;
ix := [-1,0,1]:
b := []:
for a in ix do
b := [op(b), unapply((x-a)^2,x)];
end do:
b;
[x -> (x+1)^2, x -> x^2, x -> (x-1)^2]
It is inefficient to construct lists in this way, by repeatedly concatenating them. Since that practice scales badly in performance you really ought to drop it as a practice.
You can construct the whole list with one call to seq.
Flist := [ seq( unapply((x-a)^2,x), a in ix ) ]:
Flist;
[x -> (x+1)^2, x -> x^2, x -> (x-1)^2]
Here, Flist is a list of procedures, each of which can be evaluated at a value by calling it with an argument.
Flist[1];
x -> (x+1)^2
Flist[3](3.4);
5.76
#plot(Flist, -2..2);
Note: Procedures which display with the arrow notation x -> ... are called operators.
For fun, here are equivalents, using expressions,
Elist := [ seq( (x-a)^2, a in ix ) ]:
Elist;
[ 2 2 2]
[(x + 1) , x , (x - 1) ]
Elist[1];
2
(x + 1)
eval(Elist[3], x=3.4);
5.76
#plot(Elist, x=-2..2);
There are other ways to generate the procedures, with values of a in the body being specified programmatically. The unapply command is not the only possible mechanism, although it is the easiest for your example.
Another way is to substitute, for example,
generic := x -> (x-_dummy)^2:
Hlist := [ seq( subs(_dummy=a, eval(generic)), a in ix ) ]:
Hlist;
[x -> (x+1)^2, x -> x^2, x -> (x-1)^2]

Swift : how to work with 2D array and Calculate on them

Hi I have problem with swift 2D array and calculate on them like : multiple , sum , subtract ... but I get error like : cannot convert value type
this is my code:
let ar1: [[Int]] = [[1,2], [3, 4], [5, 6], [7, 8]]
let ar2: [[Int]] = [[2,4], [6,8],[10,12],[14,16]]
var ar3: [[Int]] = []
for i in 0...ar1.count-1
{
for j in 0...ar1.count-1
{
ar3.append(ar1[i][j] + ar2 [i][j])
}
}
I have to do it by for loop please help me
ar3 contains elements of type [Int], so consequently ar3.append(…) needs an object of type [Int]. But you're not giving it an [Int], you're giving it an Int (i.e. the result of the expression ar1[i][j] + ar2[i][j]).
If you want to sum ar1 and ar2 into [1+2, 2+4, 3+6, …]:
var ar3 = [Int]()
for i in ar1 {
for j in ar2 {
ar3.append(i + j)
}
}
(or a functional way to do the same: zip(ar1, ar2).flatMap { zip($0, $1).map(+) })
If you want to sum ar1 and ar2 into [[1+2, 2+4], [3+6, 4+8], …]:
var ar3 = [[Int]]()
for i in ar1 {
ar3.append([])
for j in ar2 {
ar3.last!.append(i + j)
}
}
(or a functional way to do the same: zip(ar1, ar2).map { zip($0, $1).map(+) })

List comprehension in Swift

The language guide has revealed no trace of list comprehension. What's the neatest way of accomplishing this in Swift? I'm looking for something similar to:
evens = [ x for x in range(10) if x % 2 == 0]
As of Swift 2.x, there are a few short equivalents to your Python-style list comprehension.
The most straightforward adaptations of Python's formula (which reads something like "apply a transform to a sequence subject to a filter") involve chaining the map and filter methods available to all SequenceTypes, and starting from a Range:
// Python: [ x for x in range(10) if x % 2 == 0 ]
let evens = (0..<10).filter { $0 % 2 == 0 }
// Another example, since the first with 'x for x' doesn't
// use the full ability of a list comprehension:
// Python: [ x*x for x in range(10) if x % 2 == 0 ]
let evenSquared = (0..<10).filter({ $0 % 2 == 0 }).map({ $0 * $0 })
Note that a Range is abstract — it doesn't actually create the whole list of values you ask it for, just a construct that lazily supplies them on demand. (In this sense it's more like Python's xrange.) However, the filter call returns an Array, so you lose the "lazy" aspect there. If you want to keep the collection lazy all the way through, just say so:
// Python: [ x for x in range(10) if x % 2 == 0 ]
let evens = (0..<10).lazy.filter { $0 % 2 == 0 }
// Python: [ x*x for x in range(10) if x % 2 == 0 ]
let evenSquared = (0..<10).lazy.filter({ $0 % 2 == 0 }).map({ $0 * $0 })
Unlike the list comprehension syntax in Python (and similar constructs in some other languages), these operations in Swift follow the same syntax as other operations. That is, it's the same style of syntax to construct, filter, and operate on a range of numbers as it is to filter and operate on an array of objects — you don't have to use function/method syntax for one kind of work and list comprehension syntax for another.
And you can pass other functions in to the filter and map calls, and chain in other handy transforms like sort and reduce:
// func isAwesome(person: Person) -> Bool
// let people: [Person]
let names = people.filter(isAwesome).sort(<).map({ $0.name })
let sum = (0..<10).reduce(0, combine: +)
Depending on what you're going for, though, there may be more concise ways to say what you mean. For example, if you specifically want a list of even integers, you can use stride:
let evenStride = 0.stride(to: 10, by: 2) // or stride(through:by:), to include 10
Like with ranges, this gets you a generator, so you'll want to make an Array from it or iterate through it to see all the values:
let evensArray = Array(evenStride) // [0, 2, 4, 6, 8]
Edit: Heavily revised for Swift 2.x. See the edit history if you want Swift 1.x.
With Swift 5, you can choose one of the seven following Playground sample codes in order to solve your problem.
#1. Using stride(from:to:by:) function
let sequence = stride(from: 0, to: 10, by: 2)
let evens = Array(sequence)
// let evens = sequence.map({ $0 }) // also works
print(evens) // prints [0, 2, 4, 6, 8]
#2. Using Range filter(_:) method
let range = 0 ..< 10
let evens = range.filter({ $0 % 2 == 0 })
print(evens) // prints [0, 2, 4, 6, 8]
#3. Using Range compactMap(_:) method
let range = 0 ..< 10
let evens = range.compactMap({ $0 % 2 == 0 ? $0 : nil })
print(evens) // prints [0, 2, 4, 6, 8]
#4. Using sequence(first:next:) function
let unfoldSequence = sequence(first: 0, next: {
$0 + 2 < 10 ? $0 + 2 : nil
})
let evens = Array(unfoldSequence)
// let evens = unfoldSequence.map({ $0 }) // also works
print(evens) // prints [0, 2, 4, 6, 8]
#5. Using AnySequence init(_:) initializer
let anySequence = AnySequence<Int>({ () -> AnyIterator<Int> in
var value = 0
return AnyIterator<Int> {
defer { value += 2 }
return value < 10 ? value : nil
}
})
let evens = Array(anySequence)
// let evens = anySequence.map({ $0 }) // also works
print(evens) // prints [0, 2, 4, 6, 8]
#6. Using for loop with where clause
var evens = [Int]()
for value in 0 ..< 10 where value % 2 == 0 {
evens.append(value)
}
print(evens) // prints [0, 2, 4, 6, 8]
#7. Using for loop with if condition
var evens = [Int]()
for value in 0 ..< 10 {
if value % 2 == 0 {
evens.append(value)
}
}
print(evens) // prints [0, 2, 4, 6, 8]
Generally, a list comprehension in Python can be written in the form:
[f(x) for x in xs if g(x)]
Which is the same as
map(f, filter(g, xs))
Therefore, in Swift you can write it as
listComprehension<Y>(xs: [X], f: X -> Y, g: X -> Bool) = map(filter(xs, g), f)
For example:
map(filter(0..<10, { $0 % 2 == 0 }), { $0 })
As of Swift 2 you can do something like this:
var evens = [Int]()
for x in 1..<10 where x % 2 == 0 {
evens.append(x)
}
// or directly filtering Range due to default implementations in protocols (now a method)
let evens = (0..<10).filter{ $0 % 2 == 0 }
Got to admit, I am surprised nobody mentioned flatmap, since I think it's the closest thing Swift has to list (or set or dict) comprehension.
var evens = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].flatMap({num -> Int? in
if num % 2 == 0 {return num} else {return nil}
})
Flatmap takes a closure, and you can either return individual values (in which case it will return an array with all of the non-nil values and discard the nils) or return array segments (in which case it will catenate all of your segments together and return that.)
Flatmap seems mostly (always?) to be unable to infer return values. Certainly, in this case it can't, so I specify it as -> Int? so that I can return nils, and thus discard the odd elements.
You can nest flatmaps if you like. And I find them much more intuitive (although obviously also a bit more limited) than the combination of map and filter. For example, the top answer's 'evens squared', using flatmap, becomes,
var esquares = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].flatMap({num -> Int? in
if num % 2 == 0 {return num * num} else {return nil}
})
The syntax isn't quite as one-line not-quite-the-same-as-everything-else as python's is. I'm not sure if I like that less (because for the simple cases in python it's very short and still very readable) or more (because complex cases can get wildly out of control, and experienced python programmers often think that they're perfectly readable and maintainable when a beginner at the same company can take half an hour to puzzle out what it was intended to do, let alone what it's actually doing.)
Here is the version of flatMap from which you return single items or nil, and here is the version from which you return segments.
It's probably also worth looking over both array.map and array.forEach, because both of them are also quite handy.
One aspect of list comprehension that hasn't been mentioned in this thread is the fact that you can apply it to multiple lists' Cartesian product. Example in Python:
[x + y for x in range(1,6) for y in range(3, 6) if x % 2 == 0]
… or Haskell:
[x+y | x <- [1..5], y <- [3..5], x `mod` 2 == 0]
In Swift, the 2-list equivalent logic is
list0
.map { e0 in
list1.map { e1 in
(e0, e1)
}
}
.joined()
.filter(f)
.map(g)
And we'd have to increase the nesting level as the number of lists in input increases.
I recently made a small library to solve this problem (if you consider it a problem). Following my first example, with the library we get
Array(1...5, 3...5, where: { n, _ in n % 2 == 0}) { $0 + $1 }
The rationale (and more about list comprehension in general) is explained in an blog post.
One way would be :
var evens: Int[]()
for x in 0..<10 {
if x%2 == 0 {evens += x} // or evens.append(x)
}
Range operators
Arrays
Here's an extension to the Array types that combines filter and map into one method:
extension Array {
func filterMap(_ closure: (Element) -> Element?) -> [Element] {
var newArray: [Element] = []
for item in self {
if let result = closure(item) {
newArray.append(result)
}
}
return newArray
}
}
It's similar to map except you can return nil to indicate that you don't want the element to be added to the new array. For example:
let items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let newItems = items.filterMap { item in
if item < 5 {
return item * 2
}
return nil
}
This could also be written more concisely as follows:
let newItems = items.filterMap { $0 < 5 ? $0 * 2 : nil }
In both of these examples, if the element is less than 5, then it is multiplied by two and added to the new array. If the closure returns nil, then the element is not added. Therefore, newIems is [2, 4, 6, 8].
Here's the Python equivalent:
items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
newItems = [n * 2 for n in items if n < 5]