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 :)
Related
I need to evaluate a dynamic logical expression and I know that in ABAP it is not possible.
I found the class cl_java_script and with this class I could achieve my requeriment. I've try something like this:
result = cl_java_script=>create( )->evaluate( `( 1 + 2 + 3 ) == 6 ;` ).
After the method evaluate execution result = true as espected. But my happiness is over when I look into the class documentation that says This class is obsolete.
My question is, there is another way to achieve this?
Using any turing complete language to parse a "dynamic logical expression" is a terrible idea, as an attacker might be able to run any program inside your expression, i.e. while(true) { } will crash your variant using cl_java_script. Also although I don't know the details of cl_java_script, I assume it launches a separate JS runtime in a separate thread somewhere, this does not seem to be the most efficient choice to calculate such a small dynamic expression.
Instead you could implement your own small parser. This has the advantage that you can limit what it supports to the bare minimum whilst being able to extend it to everything you need in your usecase. Here's a small example using reverse polish notation which is able to correctly evaluate the expression you've shown (using RPN simplifies parsing a lot, though for sure one can also build a full fledged expression parser):
REPORT z_expr_parser.
TYPES:
BEGIN OF multi_value,
integer TYPE REF TO i,
boolean TYPE REF TO bool,
END OF multi_value.
CLASS lcl_rpn_parser DEFINITION.
PUBLIC SECTION.
METHODS:
constructor
IMPORTING
text TYPE string,
parse
RETURNING VALUE(result) TYPE multi_value.
PRIVATE SECTION.
DATA:
tokens TYPE STANDARD TABLE OF string,
stack TYPE STANDARD TABLE OF multi_value.
METHODS pop_int
RETURNING VALUE(result) TYPE i.
METHODS pop_bool
RETURNING VALUE(result) TYPE abap_bool.
ENDCLASS.
CLASS lcl_rpn_parser IMPLEMENTATION.
METHOD constructor.
" a most simple lexer:
SPLIT text AT ' ' INTO TABLE tokens.
ASSERT lines( tokens ) > 0.
ENDMETHOD.
METHOD pop_int.
DATA(peek) = stack[ lines( stack ) ].
ASSERT peek-integer IS BOUND.
result = peek-integer->*.
DELETE stack INDEX lines( stack ).
ENDMETHOD.
METHOD pop_bool.
DATA(peek) = stack[ lines( stack ) ].
ASSERT peek-boolean IS BOUND.
result = peek-boolean->*.
DELETE stack INDEX lines( stack ).
ENDMETHOD.
METHOD parse.
LOOP AT tokens INTO DATA(token).
IF token = '='.
DATA(comparison) = xsdbool( pop_int( ) = pop_int( ) ).
APPEND VALUE #( boolean = NEW #( comparison ) ) TO stack.
ELSEIF token = '+'.
DATA(addition) = pop_int( ) + pop_int( ).
APPEND VALUE #( integer = NEW #( addition ) ) TO stack.
ELSE.
" assumption: token is integer
DATA value TYPE i.
value = token.
APPEND VALUE #( integer = NEW #( value ) ) TO stack.
ENDIF.
ENDLOOP.
ASSERT lines( stack ) = 1.
result = stack[ 1 ].
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
" 1 + 2 + 3 = 6 in RPN:
DATA(program) = |1 2 3 + + 6 =|.
DATA(parser) = NEW lcl_rpn_parser( program ).
DATA(result) = parser->parse( ).
ASSERT result-boolean IS BOUND.
ASSERT result-boolean->* = abap_true.
SAPs BRF is an option, but potentially massive overkill in your scenario.
Here is a blog on calling BRF from abap.
And here is how Rules/Expressions can be defined dynamically.
BUT, if you know enough about the source problem to generate
1 + 2 + 3 = 6
Then it is hard to imagine why a simple custom parser cant be used.
Just how complex should the expressions be ?
Id probably write my own parser before investing in calling BRF.
Since some/many BSPs use server side JAVAscript and not ABAP as the scripting language, i cant see SAP removing the Kernel routine anytime soon.
SYSTEM-CALL JAVA SCRIPT EVALUATE.
SO maybe consider Just calling the cl_java_script anyway until it is an issue.
Then worry about a parser if and when it is really no longer a valid call.
But definitely some movement in the obsolete space here.
SAP is pushing/forcing you to cloud with the SDK, to execute such things.
https://sap.github.io/cloud-sdk/docs/js/overview-cloud-sdk-for-javascript
Is it possible to add elements to an instance of struct.
TYPE StructBase:
STRUCT
Start : INT;
Complete : INT;
END_STRUCT
END_TYPE
StructDerived : StructBase;
StructDerived.StateInit : INT;
StructDerived.StateMoveFwd : INT;
The elements of StructDerived are going to be numbered in an 'enum' function.
On completion the StructDerived elements are used for a case statement.
The idea is to have a complete abstract function ready with the basic functionalities i require. When this function is extended, it should be clear where what to add. so all units in the machine for example all have the same lay-out.
What i was looking into was something like this:
TYPE BaseState:
STRUCT
EnumVal : INT;
Name : STRING;
END_STRUCT
END_TYPE
TYPE StructBase:
STRUCT
StateArray : ARRY [..] OF BaseState;
END_STRUCT
END_TYPE
StructDerived : StructBase;
StructDerived[0].Name := 'StateInit';
StructDerived[1].Name := 'StateMoveFwd';
So if i would use it in a case:
CASE AbortingState OF
StructDerived[0].EnumVal:
....
The 'Name' is logged for state tracking. Looks like a lot of work where something like a list or dictionary would do the trick.
Thanks for any advice in this matter.
First, you cannot use strings in CASE. Only ANY_NUM or ANY_BYTE general types. So, if you want to use enumeration, you will use it like this.
TYPE EN_STEPS: (
enStateInit,
enStateMoveFwd
);
END_TYPE
TYPE BaseState:
STRUCT
EnumVal : INT;
Step : EN_STEPS;
END_STRUCT
END_TYPE
GLOBAL_VAR
StateArray : ARRAY [1..c_TOTAL] OF BaseState :=
(EnumVal := 0, Step := enStateInit),
(EnumVal := 0, Step := enStateMoveFwd);
END_VAR
GLOBAL_VAR CONSTANT
c_TOTAL: INT := 2;
END_VAR
Then in program
VAR
currentStep : EN_STEPS;
END_VAR
CASE currentStep OF
StateArray[0].Step:
//do something
END_CASE
But this is not exactly clear why you are doing this. Looks like there is not a practical application for that. You could simply do
TYPE EN_STEPS: (
enStateInit,
enStateMoveFwd
);
END_TYPE
VAR
currentStep : EN_STEPS;
END_VAR
CASE currentStep OF
enStateInit:
//do something
END_CASE
The same thing.
Second, if you want to make a universal function that accepts a different number of elements you must use pointers. This is a general answer, if you edit your question and describe what you want to make in general, I will edit my answer and extend it with a solution.
Structured Text is a strongly typed language and it is not possible to dynamically add elements to a struct at runtime.
A data structure is not a function,therefor you should design a Function Block instead of a Struct if you intend to have a
complete abstract function ready with the basic functionalities i
require
"case of" instructions have one conditional variable which is checked against one or many conditional instructions (integer literals).
You can't use variables as your conditional instructions.
You can use inheritance to extend Structs and Function Blocks in order to create a hierarchy of different modules and instatiate them in different occasions for different purposes.
var b = "pp.specifications.full_specs.";
var c = arr[i];
here the value of arr[i] is Memory
var a = b+c;
console.log(a);
it prints pp.specifications.full_specs.Memory on console
but when I use
console.log(pp.specifications.full_specs.Memory);
then prints an json object as:
{ Series: 'Inspiron',
Model: 'A562103SIN9',
Utility: 'Everyday Use',
OS: 'Windows 10 Home (64-bit)',
Dimensions: '274.73 x 384.9 x 25.44 mm',
Weight: '2.62 Kg',
Warranty: '1 Year Onsite Warranty' }
whenever the value of a contains pp.specifications.full_specs.Memory;
So what is the reason for getting different outputs?
There's a elementary difference between
console.log(pp.specifications.full_specs.Memory);
and
console.log("pp.specifications.full_specs.Memory");
Note the quotes!
So the expression: console.log(pp.specifications.full_specs.Memory); can be read from left to right:
Print to the output value of pp.specifications.full_specs.Memory and this is a value of pp object after taking its property specifications and then its property full_specs etc.
And the "pp.specifications.full_specs.Memory" means only a piece of text.
What is happening in your code should now be clearer:
// B is piece of text
var b = "pp.specifications.full_specs.";
// C is some other value - let's say also textual one
var c = arr[i]
// So b+c will mean add text to some other text
// It's expected that the result of such operations is concatenated text
// So then console.log(b+c) will mean
// Print the concatenated text from b and c
// And it's just plain text
console.log(b+c);
//It's the same as:
console.log("pp.specifications.full_specs.Memory");
// And this one means print some specific object attributes
// which is different operation!
console.log(pp.specifications.full_specs.Memory);
If you want to access objects by text you can do the following:
var d = eval(b+c);
It's pretty dangerous and eval should be avoided but I just wanted to demonstrate the basic idea. Eval executes strings as if they were code.
So string "pp.specifications.full_specs.Memory" will be evaluates (yep!) as value of the actual object.
As The eval can execute anything it should be always avoided and moreover it's superslow!
Instead if you want to access some property of pp basic on some text input you can do:
pp['specifications']['full_specs']['Memory']
As the pp.x notation is equivalent to expression: pp['x']
I hope my answer will help you understand Javascript mechanisms better :)
The following code prints "false":
a := 'aaa'.
b := a deepCopy.
Transcript show: (a == b).
I do expect this behavior and my explanation to this would be that deepCopy returns a new object "b" that is a completely different object than "a" and since operator "==" compares by reference the result is "false". Is that correct?
However, I do not understand why the following code produces "true":
a := 'aaa'.
b := 'aaa'.
Transcript show: (a == b).
Here we made two assignments to two different objects, "a" and "b", and there shouldn't be any relation between them except the fact that they contain the same value. But if operator "==" compares by reference and not by value, why is the result of this comparison "true"?
The same misconception in both cases is that the question is not "what happens?", but "what is guaranteed?". The key is that there is no guarantee that 'aaa' == 'aaa', but the compiler and VM are free to do things that way. The same seems true for the case of copying; since strings are immutable, I guess there's nothing to say that copying a string couldn't return the same object!
In your first example, as usual, the best teacher is the image. #deepCopy delegates to #shallowCopy, which at some point evaluates class basicNew: index, and copies the characters into the new object. So, this particular implementation will always create a new object.
In addition to what Sean DeNigris said, the reason why the comparison is true in the second case is that when you execute all three statements together, the compiler wants to be smart and only once creates the object for 'aaa' and shares them for a and b.
The same happens if you put this into one method *:
Object subclass: #MyClassA
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'MyApp'
!MyClassA methodsFor: 'testing' stamp: nil prior: nil!
testStrings
| a b |
a := 'aaa'
b := 'aaa'
^ a == b
! !
MyClassA testStrings " ==> true"
But this does not happen if they are in different methods:
Object subclass: #MyClassB
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'MyApp'
!MyClassB methodsFor: 'testing' stamp: nil prior: nil!
a
| a |
a := 'aaa'
^ a
! !
!MyClassB methodsFor: 'testing' stamp: nil prior: nil!
b
| b |
b := 'aaa'
^ b
! !
!MyClassB methodsFor: 'testing' stamp: nil prior: nil!
testStrings
^ self a == self b
! !
MyClassB testStrings " ==> false"
That is because in Squeak, literal objects like stings are stored in the method object of the method they are defined in
*: Technically, every DoIt or PrintIt, that is when you just execute code by keystroke, gets compiled to one method in Squeak.
This is what I know from one of the free Smalltalk books scattered online but I can't find the reference:
As you would expect the instance of a class is a unique object in memory. deepCopy intentionally creates an object first and then stores a copy of the existing instance in it.
However numbers, characters and strings are treated as primitive data types by Smalltalk. When literal data, also referred to as literals, are assigned to variables they are first checked against a local scope dictionary which is invisible to the user and holds literals to check if they have been already added to it. If they haven't they will be added to the dictionary and the variable will point to the dictionary field. If identical literal data has been assigned before, the new variable will only point to the local scope dictionary field that contains the identical literal. This means that two or more variables assigned identical literals are pointing to the same dictionary field and therefore are identical objects. This is why the second comparison in your question is returning true.
Language is AS3, but it shouldn't matter.
Almost all of the code can be ignored, I just had a series of specific questions regarding function formatting I've never seen, but found in this function while poking around bulkLoader:
The following questions relate to the subsequent code (which is edited down heavily and makes no sense), no further understanding of the code is required other than to answer what is asked for in the question (which are nearly 100% syntax):
Chunk 1, Question 1: what does it mean to assign a datatype * in a parameter?
Chunk 2, Question 2: is !url the same as checking url parameter for a null value?
Chunk 3, Question 3: What is the purpose of this step? I don't know what it means to assign a value with an "or statement" (maybe not what it means in this context?), or what an empty "{}" signifies (alternative to blank array "[]"?). Is it what allows for inputting parameters with string ID's when it's called (i.e. chunk 5)
Chunk 4, Question 4: How is the function suddenly addressing the parameter as an array? Is this part of what Chunk 3 did? I assume this is how values are set by "id" in chunk 5.
Chunk 5, No Question. This is the function call, and it calls for ID and priority (not shown in function), but it seems that the user can input any number of parameters, so long as they are labelled as function({foo: blah, bar: bleh}) with matching string names in the function as props["foo", "bar"].
Does anyone know what's going on here? It seems quite useful but I just don't understand the syntax enough to have a clue.
//chunk 1
public function add(url : *, props : Object= null ) : LoadingItem {
//chunk 2
if(!url || !String(url)){
throw new Error("[BulkLoader] Cannot add an item with a null url")
}
//chunk 3
props = props || {};
//chunk 4
if (!props["id"] && _allowsAutoIDFromFileName){
props["id"] = getFileName(url.url);
log("Adding automatic id from file name for item:", item , "( id= " + props["id"] + " )");
}
}
//chunk 5
bulkLoader.add("images/image1.jpg", {id:"item1", priority:100});
Question 1: * means that it can recive any Datatype as parameter
Question 2: yes, its the same
Question 3: it takes the Object if it exists OR makes a new Object. {} == new Object()
Question 4: Is just another way of addresing a property in an Object. props["id"] == props.id