How to Define a Custom Subscripting Array Operator Which Makes Array Elements "Spring into Existence" If Necessary

how to define a custom subscripting array operator which makes array elements spring into existence if necessary

This isn’t related to the subscript operator, but more a question of how to define a ??= operator. Which you can do, but it might not work quite the way you expect.

Here’s a possible implementation:

// first define the ??= operator
infix operator ??= { }

// then some pretty standard logic for an assignment
// version of ??
func ??=<T>(inout lhs: T?, rhs: T) {
lhs = lhs ?? rhs
}

This compiles, and works as you might be expecting:

var i: Int? = 1

i ??= 2 // i unchanged

var j: Int? = nil

j ??= 2 // j is now Some(2)

It will also work in combination with subscripts:

var a: [Int?] = [1, nil]

a[1] ??= 2

a[1] // is now Some(2)

I say this might not work completely as expected because of the types. a ?? b takes an optional a, and if it’s nil, returns a default of b. However it returns a non-optional value. That’s kinda the point of ??.

On the other hand, ??= cannot do this. Because the left-hand side is already determined to be optional, and an assignment operator cannot change the type only the value. So while it will substitute the value inside the optional in the case of nil, it won’t change the type to be non-optional.

PS the reason the ??= function compiles at all is because non-optional values (i.e. what you will get back from lhs ?? rhs) are implicitly upgraded to optional values if necessary, hence lhs ?? rhs, of type T, can be assigned to lhs, which is of type T?.

PHP: Access Array Value on the Fly

I wouldn't bother about that extra variable, really. If you want, though, you could also remove it from memory after you've used it:

$variable = array('a','b','c');
echo $variable[$key];
unset($variable);

Or, you could write a small function:

function indexonce(&$ar, $index) {
return $ar[$index];
}

and call this with:

$something = indexonce(array('a', 'b', 'c'), 2);

The array should be destroyed automatically now.

Why array type object is not modifiable?

Assume the declaration

int a[10];

then all of the following are true:

  • the type of the expression a is "10-element array of int"; except when a is the operand of the sizeof or unary & operators, the expression will be converted to an expression of type "pointer to int" and its value will be the address of the first element in the array;
  • the type of the expression a[i] is int; it refers to the integer object stored as the i'th element of the array;
  • The expression a may not be the target of an assignment because C does not treat arrays like other variables, so you cannot write something like a = b or a = malloc(n * sizeof *a) or anything like that.

You'll notice I keep emphasizing the word "expression". There's a difference between the chunk of memory we set aside to hold 10 integers and the symbols (expressions) we use to refer to that chunk of memory. We can refer to it with the expression a. We can also create a pointer to that array:

int (*ptr)[10] = &a;

The expression *ptr also has type "10-element array of int", and it refers to the same chunk of memory that a refers to.

C does not treat array expressions (a, *ptr) like expressions of other types, and one of the differences is that an expression of array type may not be the target of an assignment. You cannot reassign a to refer to a different array object (same for the expression *ptr). You may assign a new value to a[i] or (*ptr)[i] (change the value of each array element), and you may assign ptr to point to a different array:

int b[10], c[10];
.....
ptr = &b;
.....
ptr = &c;

As for the second question...

An incomplete type lacks size information; declarations like

struct foo;
int bar[];
union bletch;

all create incomplete types because there isn't enough information for the compiler to determine how much storage to set aside for an object of that type. You cannot create objects of incomplete type; for example, you cannot declare

struct foo myFoo;

unless you complete the definition for struct foo. However, you can create pointers to incomplete types; for example, you could declare

struct foo *myFooPtr;

without completing the definition for struct foo because a pointer just stores the address of the object, and you don't need to know the type's size for that. This makes it possible to define self-referential types like

struct node {
T key; // for any type T
Q val; // for any type Q
struct node *left;
struct node *right;
};

The type definition for struct node isn't complete until we hit that closing }. Since we can declare a pointer to an incomplete type, we're okay. However, we could not define the struct as

struct node {
... // same as above
struct node left;
struct node right;
};

because the type isn't complete when we declare the left and right members, and also because each left and right member would each contain left and right members of their own, each of which would contain left and right members of their own, and on and on and on.

That's great for structs and unions, but what about

int bar[];

???

We've declared the symbol bar and indicated that it will be an array type, but the size is unknown at this point. Eventually we'll have to define it with a size, but this way the symbol can be used in contexts where the array size isn't meaningful or necessary. Don't have a good, non-contrived example off the top of my head to illustrate this, though.

EDIT

Responding to the comments here, since there isn't going to be room in the comments section for what I want to write (I'm in a verbose mood this evening). You asked:

Does it mean every variables are expression?

It means that any variable can be an expression, or part of an expression. Here's how the language standard defines the term expression:

6.5 Expressions
1 An expression is a sequence of operators and operands that specifies computation of a
value, or that designates an object or a function, or that generates side effects, or that
performs a combination thereof.

For example, the variable a all by itself counts as an expression; it designates the array object we defined to hold 10 integer values. It also evaluates to the address of the first element of the array. The variable a can also be part of a larger expression like a[i]; the operator is the subscript operator [] and the operands are the variables a and i. This expression designates a single member of the array, and it evaluates to the value currectly stored in that member. That expression in turn can be part of a larger expression like a[i] = 0.

And also let me clear that, in the declaration int a[10], does a[] stands for array type

Yes, exactly.

In C, declarations are based on the types of expressions, rather than the types of objects. If you have a simple variable named y that stores an int value, and you want to access that value, you simply use y in an expression, like

x = y;

The type of the expression y is int, so the declaration of y is written

int y;

If, on the other hand, you have an array of int values, and you want to access a specific element, you would use the array name and an index along with the subscript operator to access that value, like

x = a[i];

The type of the expression a[i] is int, so the declaration of the array is written as

int arr[N]; // for some value N.  

The "int-ness" of arr is given by the type specifier int; the "array-ness" of arr is given by the declarator arr[N]. The declarator gives us the name of the object being declared (arr) along with some additional type information not given by the type specifier ("is an N-element array"). The declaration "reads" as

    a       -- a
a[N] -- is an N-element array
int a[N]; -- of int

EDIT2

And after all that, I still haven't told you the real reason why array expressions are non-modifiable lvalues. So here's yet another chapter to this book of an answer.

C didn't spring fully formed from the mind of Dennis Ritchie; it was derived from an earlier language known as B (which was derived from BCPL).1 B was a "typeless" language; it didn't have different types for integers, floats, text, records, etc. Instead, everything was simply a fixed length word or "cell" (essentially an unsigned integer). Memory was treated as a linear array of cells. When you allocated an array in B, such as

auto V[10];

the compiler allocated 11 cells; 10 contiguous cells for the array itself, plus a cell that was bound to V containing the location of the first cell:

    +----+
V: | | -----+
+----+ |
... |
+----+ |
| | <----+
+----+
| |
+----+
| |
+----+
| |
+----+
...

When Ritchie was adding struct types to C, he realized that this arrangement was causing him some problems. For example, he wanted to create a struct type to represent an entry in a file or directory table:

struct {
int inumber;
char name[14];
};

He wanted the structure to not just describe the entry in an abstract manner, but also to represent the bits in the actual file table entry, which didn't have an extra cell or word to store the location of the first element in the array. So he got rid of it - instead of setting aside a separate location to store the address of the first element, he wrote C such that the address of the first element would be computed when the array expression was evaluated.

This is why you can't do something like

int a[N], b[N];
a = b;

because both a and b evaluate to pointer values in that context; it's equivalent to writing 3 = 4. There's nothing in memory that actually stores the address of the first element in the array; the compiler simply computes it during the translation phase.


1. This is all taken from the paper The Development of the C Language

Array of Objects in Spring Boot REST API

Take a look at the following:

Whenever I add a single object to an array, it replaces all the objects that were previously in the array. For instance, I have two objects in the "Spring 2020" array. If I make another POST request with addCourse(), both the objects get replaced.

The problem is that you are using map.put(). When using this method, if the map previously contained a mapping for the key, the old value is replaced by the specified value. Hence, your old values are removed.

The solution is to get and update the existing entry.

public void add( Map<String, List<Courses>> course) {
course.forEach((semester, list) -> {
if(courseList.contains(semester)) {
courseList.get(semester).addAll(list); // Note, list accepts duplicates
} else {
courseList.put(semester, list);
}
});
}

If I try to edit using postman, I get some exception.
PUT: localhost:8080/edit/Spring20/
"message": "Missing URI template variable 'semester' for method parameter of type String",

The problem is that your path variable name semester does not match the name in the url semster. Hence, Missing URI template variable 'semester'

@RequestMapping(method=RequestMethod.PUT, value="/edit/{semester}/{id}")
public void addCourse(@PathVariable("semester") String semester, @PathVariable("id") String id, @RequestBody Courses course) {
service.editTest(semester, course);
}

Spring Data Mongo Template - Counting an array

It's quite possible, the $size operator is supported (see DATAMONGO-979 and its implementation here). Your implementation could follow this example:

import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

Aggregation agg = new Aggregation(
match(where("_id").is("1")), //
project() //
.and("array") //
.size() //
.as("count")
);

AggregationResults<IntegerCount> results = mongoTemplate.aggregate(
agg, collectionName, Integer.class
);
List<IntegerCount> intCount = results.getMappedResults();


Related Topics



Leave a reply



Submit