Sorting multidimensional array in Delphi 2007 - string

I have a multidimensional array similar to the one described below:
Matrix => array(
[0] => array( [0] => 7, [1] => 'hello' ),
[1] => array( [0] => 3, [1] => 'there' ),
[2] => array( [0] => 1, [1] => 'world' ),
)
What I'm trying to achieve is to sort this array using the values of Matrix[i][0]. What would be the most efficient way to do this? I looked into the recursive QuickSort function possibility, but it's rather complicated as I'm multidimensional arrays. There are many examples of quicksort, but none of them handle taking an "Array of Array of String" as an input.
Please ask for clarification if my text seems gibberish. I'm still fairly new to Delphi.

As Rob pointed out in his comment, there's no way to have an multi-dimensional array that stores both integers and strings (without resorting to variants).
If it's really just an array containing integer/string pairs, it would be much easier to store them in a TStringList using the Strings and Objects properties, and then use CustomSort to sort on the Object values:
program Project2;
{$APPTYPE CONSOLE}
uses
SysUtils, Classes;
function MySortProc(List: TStringList; Index1, Index2: Integer): Integer;
begin
Result := Integer(List.Objects[Index1]) - Integer(List.Objects[Index2]);
end;
var
SL: TStringList;
i: Integer;
begin
SL := TStringList.Create;
try
for i := 0 to 10 do
SL.AddObject(Format('Item %d', [i]), TObject(Random(i)));
SL.CustomSort(#MySortProc);
for i := 0 to SL.Count - 1 do
WriteLn(Format('%d: %s', [Integer(SL.Objects[i]), SL[i]]));
ReadLn;
finally
SL.Free;
end;
end.
This produces

First of all I question whether or not you have chosen the correct type to represent your data structure. You say your type is
array of array of string
But it looks like the inner array always has exactly two elements, the first an integer, and the second a string. In that case the inner array should be replaced with a record.
type
TMyElement = record
ID: Integer;
Name: string;
end;
TMyArray = array of TMyElement;
And now you just have a one dimensional array. I assume you have no difficulty sorting one of those.
But suppose that you really did need a multi-dimensional array. Suppose that the array was ragged, i.e. that different inner arrays had different lengths. You can still view that array as a one-dimensional array. Declare it like this:
TStringArray = array of string;
TMyArray = array of TStringArray;
Now you can sort TMyArray just as if it were a one-dimensional array.
Note that I needed to declare a type for the inner array. The reason being that the sort function needs to be able to compare and exchange elements of the outer array. So you'll need functions to do that. And you need to define a type to achieve that. For example, your exchange function might look like this:
procedure Exchange(Index1, Index2: Integer);
var
temp: TStringArray;
begin
temp := MyArray[Index1];
MyArray[Index1] := MyArray[Index2];
MyArray[Index2] := temp;
end;
Without defining TStringArray, this would not be possible. That's because of the rather stringent assignment compatibility rules for dynamic arrays.
You can extend to as many dimensions as you like:
TString2DArray = array of TStringArray;
TMyArray = array of TString2DArray;
Again, you can use your standard array sort to sort this three dimensional version of TMyArray.

Related

From VBA to Delphi conversion (Optional arguments issue)

At the moment I'm converting a project written in VBA to Delphi and have stumbled upon a problem with converting some Subs with Optional arguments.
Say, there is a Sub declaration (just an example, actual Subs have up to 10 optional parameters):
Sub SetMark
(x0 As Double, y0 As Double,
Optional TextOffset As Integer =5,
Optional TextBefore As String = "",
Optional Text As String = "",
Optional TextAfter As String = "mm",
Optional Color As String = "FFFFFF",
Optional ArrowPresent As Boolean = True)
That Sub subsequently can be called like this:
Call SetMark (15, 100,,,"135")
Call SetMark (100, 100, 8,, "My text here..", "")
'a lot of calls here
The Optional arguments are very flexible here, you can omit any of them, and you can assign a value to any of them as well. Unlike in Delphi.
Procedure SetMark
(x0: real; y0: real,
TextOffset: Integer =5;
TextBefore: ShortString = '';
Text: ShortString = '';
TextAfter: ShortString = 'mm';
Color: ShortString = 'FFFFFF';
ArrowPresent: Boolean = True);
It seems you cannot just make a copy of VBA call:
SetMark (15, 100,,,'135');// error here
So, the question is: is there any way to convert that Subs to Delphi procedures keeping the same flexibility in parameters?
My first idea was to use default parameters, but it doesn't work.
As for now it seems in Delphi I will have to pass all the parameters in the list with their values directly but that means a lot of work for reviewing and proper porting of VBA calls.
Any ideas?
Is there any way to convert the VBA subroutines to Delphi procedures, still keeping the same flexibility in parameters?
There is no way to achieve that – that flexibility to omit parameters, other than at the end of the list, simply does not exist.
For methods of automation objects, you can use named parameters, as described here: Named/optional parameters in Delphi? However, I very much recommend that you don't implement your classes as automation objects just to get that functionality.
Whenever you switch between languages, you will find differences that are inconvenient. That is inevitable. The best approach is to try to find the best way to solve the problem in the new language, rather than trying to force idioms from the old language into the new language.
In this case you might want to use overloaded functions or parameter objects as ways to alleviate this inconvenience.
Just to expand on the idea of refactoring to use a parameter object, you could declare a record like:
TSetMarkParams = record
x0 : double;
y0 : double;
TextOffset : integer;
TextBefore : string;
Text : string;
TextAfter : string;
Color : string;
ArrowPresent : boolean;
constructor Create(Ax0, Ay0 : double);
end;
And implement the constructor to populate default values as :
constructor TSetMarkParams.Create(Ax0, Ay0 : double);
begin
x0 := Ax0;
y0 := Ay0;
TextOffset := 5;
TextBefore := '';
Text := '';
TextAfter := 'mm';
Color := 'FFFFFF';
AllowPresent := true;
end;
Your procedure would then have signature :
procedure SetMark(ASetMarkParams : TSetMarkParams);
Which you could then, using your example of SetMark (15, 100,,,'135'); call as :
var
LSetMarkParams : TSetMarkParams
begin
LSetMarkParams := TSetMarkParams.Create(15, 100);
LSetMarkParams.Text := '135';
SetMark(LSetMarkParams);
end;
As collateral benefit, the above is much more readable as it saves you from going blind trying to count commas when returning to debug a troublesome method call.

is there builtin hashmap function in Isabelle?

In perl there are hashes, key-value pairs. Does Isabelle/HOL have a builtin such function with the corresponding theorems?
Typically in Isabelle/HOL, you would just use a function of type 'key ⇒ 'value. For instance:
definition "num_animals
≡ (λ_. 0)(''dog'' := 3, ''cat'' := 42, ''mouse'' := 12)"
lemma "num_animals ''dog'' = 3"
by (simp add: num_animals_def)
Here, (λ_. 0) is a function that returns 0 for all input values, while the syntax (''dog'' := 3) modifies the existing function so that the input dog returns 3.
If you want to be able to determine if a function does not contain a particular key, you can use Isabelle/HOL's option type:
definition "num_animals
≡ (λ_. None)(''dog'' := Some 3, ''cat'' := Some 42, ''mouse'' := Some 12)"
lemma "num_animals ''elephant'' = None"
by (simp add: num_animals_def)
There are numerous differences between this and a hash-table. For example, Isabelle will have to perform at least O(n) steps to look up a value in the function in this format. Also, no hashing is going on: key values are stored and compared explicitly. Usually, though, because you are reasoning about these things, and not typically trying to execute these things, this is not a problem.
If you are dealing with larger proof terms, you may need to look into other representations of functions, such as Binary Search Trees in the AFP, though these will be harder to work with.

Delphi: Executing Conditional Statements in a String

How can I execute in Delphi a conditional statements in a String?
In PHP there is something like this:
<?php
echo "Hello (isset($name) ? $name : 'Guest'));
?>
I'm assuming you actually want to evaluate code that is not known until runtime. That's the only reason why you would have code in a string. If my assumption is correct, then you cannot do that readily in Delphi. Delphi is compiled. So in order to execute Delphi code you need to compile it.
You could consider using a scripting language for this part of your program. There are many available.
Of course, if all you want is a conditional operator in Delphi then there is none built in but the RTL provides IfThen:
function IfThen(AValue: Boolean; const ATrue: string;
AFalse: string = ''): string;
Description
Conditionally returns one of two specified values.
IfThen checks the expression passed as AValue and returns ATrue if it evaluates to true, or AFalse if it evaluates to false. In Delphi, if the AFalse parameter is omitted, IfThen returns 0 or an empty string when AValue evaluates to False.
the closest thing you can get in Delphi is this :
Writeln('Hello ' + IIf(Name='', 'Guest', Name));
where IIf is defined as:
function iif(Test: boolean; TrueRes, FalseRes: string): string;
begin
if Test then
Result := TrueRes
else
Result := FalseRes;
end;
Please mind that this example only works with strings...
EDIT
As David suggested you can also use the IfThen function from the StrUtils unit
For a type independent IIF, use this:
function IIF(pResult: Boolean; pIfTrue: Variant; pIfFalse: Variant): Variant;
begin
if pResult then
Result := pIfTrue
else
Result := pIfFalse;
end;

Smalltalk - Compare two strings for equality

I am trying to compare two strings in Smalltalk, but I seem to be doing something wrong.
I keep getting this error:
Unhandled Exception: Non-boolean receiver. Proceed for truth.
stringOne := 'hello'.
stringTwo := 'hello'.
myNumber := 10.
[stringOne = stringTwo ] ifTrue:[
myNumber := 20].
Any idea what I'm doing wrong?
Try
stringOne = stringTwo
ifTrue: [myNumber := 20]`
I don't think you need square brackets in the first line
Found great explanation. Whole thing is here
In Smalltalk, booleans (ie, True or False) are objects: specifically, they're instantiations of the abstract base class Boolean, or rather of its two subclasses True and False. So every boolean has type True or False, and no actual member data. Bool has two virtual functions, ifTrue: and ifFalse:, which take as their argument a block of code. Both True and False override these functions; True's version of ifTrue: calls the code it's passed, and False's version does nothing (and vice-versa for ifFalse:). Here's an example:
a < b
ifTrue: [^'a is less than b']
ifFalse: [^'a is greater than or equal to b']
Those things in square brackets are essentially anonymous functions, by the way. Except they're objects, because everything is an object in Smalltalk. Now, what's happening there is that we call a's "<" method, with argument b; this returns a boolean. We call its ifTrue: and ifFalse: methods, passing as arguments the code we want executed in either case. The effect is the same as that of the Ruby code
if a < b then
puts "a is less than b"
else
puts "a is greater than or equal to b"
end
As others have said, it will work the way you want if you get rid of the first set of square brackets.
But to explain the problem you were running into better:
[stringOne = stringTwo ] ifTrue:[myNumber := 20]
is passing the message ifTrue: to a block, and blocks do not understand that method, only boolean objects do.
If you first evaluate the block, it will evaluate to a true object, which will then know how to respond:
[stringOne = stringTwo] value ifTrue:[myNumber := 20]
Or what you should really do, as others have pointed out:
stringOne = stringTwo ifTrue:[myNumber := 20]
both of which evaluates stringOne = stringTwo to true before sending ifTrue:[...] to it.
[stringOne = stringTwo] is a block, not a boolean. When the block is invoked, perhaps it will result in a boolean. But you are not invoking the block here. Instead, you are merely causing the block to be the receiver of ifTrue.
Instead, try:
(stringOne = stringTwo) ifTrue: [
myNumber := 20 ].
Should you be blocking the comparison? I would have thought that:
( stringOne = stringTwo ) ifTrue: [ myNumber := 20 ]
would be enough.
but I seem to be doing something wrong
Given that you are using VisualWorks your install should include a doc folder.
Look at the AppDevGuide.pdf - it has a lot of information about programming with VisualWorks and more to the point it has a lot of introductory information about Smalltalk programming.
Look through the Contents table at the beginning, until Chapter 7 "Control Structures", click "Branching" or "Conditional Tests" and you'll be taken to the appropriate section in the pdf that tells you all about Smalltalk if-then-else and gives examples that would have helped you see what you were doing wrong.
I would like to add the following 50Cent:
as blocks are actually lambdas which can be passed around, another good example would be the following method:
do:aBlock ifCondition:aCondition
... some more code ...
aCondition value ifTrue: aBlock.
... some more code ...
aBlock value
...
so the argument to ifTrue:/ifFalse: can actually come from someone else. This kind of passed-in conditions is often useful in "..ifAbsent:" or "..onError:" kind of methods.
(originally meant as a comment, but I could not get the code example to be unformatted)

How to use sets of string in Pascal?

I am writing a little game in which the user is asked for their race and class.
There are five possible races of string[5] and four possible classes of string[9].
How do I program pascal to 1. define the five races and four classes as constants, 2. check the user input to see whether the input is within the possible races and classes - without using multiple IF statements?
Any hints will be appreciated.
Since your input is strictly defined, my first question is, must you use strings as the user input? Can you not give the user a choice? Say a drop down? Then you can map each choice to an enumeration.
type
race = (rcRace0, rcRace1, rcRace2 rcRace3, rcRace4);
case race(Input) of //input is an integer, index of the drop down list for example
rcRace0 : //perform processing for race 0
rcRace1 : //perform processing for race 1
rcRace2 : //perform processing for race 2
rcRace3 : //perform processing for race 3
rcRace4 : //perform processing for race 4
end;
Same for class.
I would recommend Steves solution as the starting point, but go a bit further with the use of enumerated types and sets...
type
TRace = (rcRace0, rcRace1, rcRace2, rcRace3, rcRace4);
TCharacterClass = (ccClass0, ccClass1, ccClass2, ccClass3);
TCharacterClassSet = set of TCharacterClass;
const
validCombinations : array[TRace] of TCharacterClassSet = (
[ccClass0, ccClass1, ccClass2, ccClass3], // race0 can be any class
[ccClass0, ccClass2], // race1
[ccClass0, ccClass1, ccClass2], // race2
[ccClass0, ccClass3], // race3
[ccClass0] // race4
);
You could also set up constants for the race names and character classes:
const
raceNames : array[TRace] of string = (
'Race 0',
'Race 1',
'Race 2',
'Race 3',
'Race 4'
);
characterClassNames = array[TCharacterClass] of string = (
'Class 0',
'Class 1 ',
'Class 2',
'Class 3'
);
Now, if you use comboboxes for user input and map the input to these enumerated types, the check if a combination is valid is simple:
function ValidRaceAndClass( aRace : TRace; aClass : TCharacterClass ) : Boolean;
begin
result := aClass in validCombinations[ aRace ];
end;
I find you should follow Steves solution. But if using strings is your way, you could use a TStringList (in Delphi/possibly FreePascal). You could fill it with your races and then evaluate the players answer using the IndexOf function of the TStringList. It returns the index of the passed string or -1 when the passed string isn't in the List.
Anyway i'd strongly recommend Steves solution :)

Resources