Y/N or y/n in loop - c#

I have trouble implementing the Y/N or y/n in the loop. I've designed it in a way that a user can use both the capital and small letters of the Y and N for their answer in a loop. by the way here's my code but can't seem to make it work:
do
{
Console.WriteLine("\nSelect additional topping/s\n");
Console.WriteLine("1 - Extra meat: 200");
Console.WriteLine("2 - Extra cheese: 100");
Console.WriteLine("3 - Extra veggies: 80\n");
int selectedTopping = Convert.ToInt32(Console.ReadLine());
switch (selectedTopping)
{
case 1:
pizza = new MeatToppings(pizza);
break;
case 2:
pizza = new CheeseToppings(pizza);
break;
case 3:
pizza = new VeggieToppings(pizza);
break;
default:
break;
}
Console.WriteLine("\nAdd more toppings? Y/N");
}
while ((Console.ReadLine() == "Y") || (Console.ReadLine() == "y"));

while ((Console.ReadLine() == "Y") || (Console.ReadLine() == "y"));
This is going to read 2 different lines since you're calling ReadLine() twice. You need to call it once and save the value.

You can use ToUpper
while ((Console.ReadLine().ToUpper() == "Y") );

Try to use String.Equals and StringComparison:
String.Equals(Console.ReadLine(), "y", StringComparison.CurrentCultureIgnoreCase);
from MSDN:
CurrentCultureIgnoreCase: Compare strings using culture-sensitive sort rules, the current culture, and ignoring the case of the strings being compared.
OrdinalIgnoreCase: Compare strings using ordinal sort rules and ignoring the case of the strings being compared.

To check Y or y ignoring case, you should use string.Equals(string,StringComparison) overload.
while (Console.ReadLine().Equals("Y", StringComparison.InvariantCultureIgnoreCase));
Please see the The Turkish İ Problem and Why You Should Care before using ToUpper or ToLower for string comparison with ignore case.
Your current code is reading the lines from console twice, that is why your code is holding up for the 2nd value.

As Austin just pointed out, you are using ReadLine twice in the while loop statement.
One thing worth mentioning is try to follow the rule of modularity, it will help speed up implementing and debugging our code.
It's been a while since I did any C# programming so sudo-coding this in Java style.
Since it's a command line programming you are probably have to validate user input more than once. One thing I would do is make a utility class to contains common user input tasks.
public class TerminalUtil {
private TerminalUtil() {}
public static boolean isYes(String msg){ return (msg.ToUpper() == "Y" || msg.ToUpper() == "YES"); }
public static boolean isNo(String msg){ return (msg.ToUpper() == "N" || msg.ToUpper() == "NO"); }
// You also might want basic conditionals to check if string is numeric or contains letters.
// I like using recursion for command line utilities so having a method that can re-print messages is handy
public static void display(String[] messages){
for(String msg : messages){
Console.WriteLine(msg);
}
}
public static boolean enterYesOrNo(String[] messages, String[] errorMessages){
display(messages)
String input = Console.ReadLine();
if( isYes(input) ){
return true;
} else if( isNo(input) ){
return false;
} else {
display(errorMessages); // Maybe something like, you didn't enter a yes or no value.
enterYesOrNo(messages, errorMessages); // Recursive loop to try again.
}
}
}
Here is what the code to order a pizza might look like
public class OrderPizza{
public static int selectToppings(){
String[] message = new String[4];
message[0] = ("\nSelect additional topping/s\n");
message[1] = ("1 - Extra meat: 200");
message[2] = ("2 - Extra cheese: 100");
message[3] = ("3 - Extra veggies: 80\n");
int option = TerminalUtils.entryNumeric(message, {"You entered an non-numeric character, try again"} );
if( option > 0 && option <= 3 ){
return option;
} else {
Console.WriteLine("Number must be between 1 - 3, try again.");
return selectToppings();
}
}
public static Pizza order(){
Pizza pizza = new Pizza();
while(true){
int toppingCode = selectTopping();
pizza.addTopping(toppingCode);
if(!TerminalUtil.enterYesOrNo({"\nAdd more toppings? Y/N"}, {"Please enter a 'Y'es or 'N'o"}) ){
break;
}
}
}
}
The main benefit of this is that the business logic of the while loop has been reduced and you can reuse the code in TerminalUtils. Also this is by no mean a elegant solution, I'm lazy and it's 3am IRL, but it's should be enough to the ball rolling.
One thing you should probably reconsider doing is using integer codes to represent toppings. Using an enum might make things easier to implement.
I also notice that you add three different types of pizza's, which I'm assuming three separate objects.
Since you are looping to add toppings to a pizza, make an abstract class of pizza. This way you could extend it generic pre-built pizzas such as pepperoni or cheese and use the abstract pizza class if you want the customer to customize their order.

I didn't found better way than:
while ( str!="N" )
{
str = Console.ReadLine();
str = str.ToUpper();
if (str == "Y");
break;
};

Related

How can I reduce the amount of C# code used in this program?

So I'm trying to write a simple program for practicing purposes, I got this to work the way I want it but I still feel like I can reduce the amount of code used, any suggestions?
Thanks, in advance!
static void Main(string[] args)
{
string playerAnswer = Console.ReadLine();
string potReward = "";
string trueReward = calculateReward(playerAnswer, potReward);
Console.WriteLine("Congratulations, you won {0}! ", trueReward);
}
private static string calculateReward(string answer, string reward)
{
if (answer == "1")
{
reward = "a cat";
}
else if (answer == "2")
{
reward = "power to rival Chuck Norris";
}
else if (answer == "3")
{
reward = "a burger";
}
else
{
reward = "nothing";
}
return reward;
}
I get the feeling this is your graded homework, not just practice, in any case here is some help.
There are plenty of excellent tutorials for free online, here is one example that assumes no previous coding or C# experience.
https://learn.microsoft.com/en-us/dotnet/csharp/tutorials/intro-to-csharp/
Some starting points might be to look at:
switch statements
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/switch
int caseSwitch = 1;
switch (caseSwitch)
{
case 1:
Console.WriteLine("Case 1");
break;
case 2:
Console.WriteLine("Case 2");
break;
default:
Console.WriteLine("Default case");
break;
}
multiple variable assignment
https://www.dotnetperls.com/multiple-local-variable (general C-style guides can often apply too, though stick to C# if unsure at all)
string s = "dot", a = "net", m = "perls";
And are all your variables actually being used/useful?
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/passing-value-type-parameters
A value-type variable contains its data directly as opposed to a reference-type variable, which contains a reference to its data. Passing a value-type variable to a method by value means passing a copy of the variable to the method. Any changes to the parameter that take place inside the method have no affect on the original data stored in the argument variable. If you want the called method to change the value of the argument, you must pass it by reference, using the ref or out keyword. You may also use the in keyword to pass a value parameter by reference to avoid the copy while guaranteeing that the value will not be changed. For simplicity, the following examples use ref.
Also, if its a private function, see if actually needing to be separate, though likely it will be if part of larger solution/project.
Ive deliberately not updated your sample code, very simple to do given examples and the more you type it out yourself the more you get used to the language.
Hope that helps :)
You can also use the ternary operator for filter:
private static string calculateReward(string answer, string reward)
{
reward = answer == "1" ? "a cat" : answer == "2" ? "power to rival Chuck Norris" : answer == "3" ? "a burger" : "nothing";
return reward;
}
A switch-case statement is the best way to go on this one.
switch(answer)
{
case 1:
reward = "A cat.";
break;
case 2:
reward = "Power to rival Chuck Norris.";
break;
case 3:
reward = "A burger";
break;
default:
reward = "Nothing";
break;
}

How do i check if an input matches a specific letter?

I can't figure out how to check whether the input is a specific letter. I know how to check if it's a specific int/double but when it's a string I don't know what to do.
Any help would be greatly appreciated. I'm just trying to make a basic 3 question quiz that checks whether the user answers with the correct letter (a, b or c) and then adds that to the current score.
class Program
{
static void Main()
{
var a1 = "a";
var a2 = "b";
var a3 = "c";
var qa = 0;
while (qa != 3)
{
if (qa == 0)
{
Console.Write("What is the answer to question 1? ");
var entry1 = Console.Read();
if()
{
}
}
else if (qa == 1)
{
Console.Write("What is the answer to question 2? ");
Console.ReadLine();
}
else if (qa == 2)
{
Console.Write("What is the answer to question 3? ");
Console.ReadLine();
}
}
}
}
For example operator == can't be applied to strings
this is not true. It can be applied:
if(entry.ToString() == a1)
The documentation for the == operator tells us:
For the string type, == compares the values of the strings
another possibility would be to use the String.Equals method
if(entry.ToString().Equals(a1))
EDIT:
Looking closer at your code I realized that you are using Console.Read
which
Reads the next character from the standard input stream.
That means that it returns a char (and only 1).
I guess you want the entire line that the user types in. So you should use ReadLine instead. It returns a string and allows you for a direct comparison
string entry1 = Console.ReadLine();
if(entry == a1)
when you use var for the declaration of types the compiler infers the type and the error becomes obvious at a later stage. you cannot use the == operator on string and char . Read() returns a char so that's why you were not able to compare it in the first place
Note that in "Question 1" you wrote Console.Read(), (not Console.ReadLine()) which returns a char, not a string. So "==" cannot be applied to entry1 and a1 since entry1 will be a char while a1 is a string.
If you compare compatible variables, everything should be fine
string input1;
var input2 = "";
var input3 = 0;
// assign some input values, then compare
// strings
if (input1 == "a") // ok
{ }
if (input2 == "a") // ok
{ }
if (input3 == "a") // not ok, comparing int to string
{ }
// numbers
if (input1 == 1) // not ok, comparing string to int
{ }
if (input3 == 1) // ok
{ }
If you want to, you could try matching the ASCII value of the character by using Console.Read();. This would eliminate the need to press enter. If you were trying to match to uppercase A:
int input = Console.Read();
if (input == (int)char.GetNumericValue(A))
{
Console.WriteLine("True");
Console.Read();
}
else
{
Console.WriteLine("False");
Console.Read();
}

Exception handling during runtime in array with C#

I'm beginner in programming and I have tried some simple code in C#. [1]: http://i.stack.imgur.com/zLVbz.jpg
This code deals with simple array initialization ,storing and sorting and so on.Its runs as usual in the first try but when I again want to store in the array it throws the exception which I don't understand.
[![Its the exception I'm getting][1]][1]
static void Main(string[] args)
{
int number;
char y;
string[] answer = new string[10];
bool keeprompting = true;
while (keeprompting)
{
Console.WriteLine("Enter the options given below 1.Add students\n 2.View all details\n 3.Sorting\n 4.Exit\n");
int input = Convert.ToInt16(Console.ReadLine());
switch (input) {
case 1:
Console.WriteLine("Enter the Number of Students to be added to the List");
number = Convert.ToInt16(Console.ReadLine()); **exception **
for (int i = 0; i < number; i++) {
answer[i] = Console.ReadLine();
}
break;
case 2:
foreach(var item in answer)
{
Console.WriteLine(item.ToString());
}
break;
case 3:
Array.Sort(answer);
foreach(var item in answer)
{
Console.WriteLine(item.ToString()); **exception **
}
break;
case 4:
Console.WriteLine("Are you sure you want to exit");
Console.WriteLine("1 for Yes and for No");
y = (char) Console.Read();
if (y != 1) {
keeprompting = true;
} else {
keeprompting = false;
}
Console.WriteLine("thank you");
break;
}
}
}
Any and all suggestion are welcome.
1) Possibly FormatException raised here int input = Convert.ToInt16(Console.ReadLine()); because you enter to the Console not '1', '2', etc., but '1.', '2.' and so on. Or may be you use other symbols that cannot be parsed using Convert.ToInt16() method. Value you enter should be in range -32768..32767 and should not contain any space, dot, comma and other symbols except minus sign.
2) The same may happen here number = Convert.ToInt16(Console.ReadLine()); **exception**
3) And here I think you get NullReferenceException:
Array.Sort(answer);
foreach (var item in answer)
{
Console.WriteLine(item.ToString()); **exception**
}
It happens because you have 10 items in your array (string[] answer = new string[10];), but when you insert students you may insert less than 10, so you have for exapmle 3 items initialized, and other set to its default value - null for string. So your array looks like this: { "John", "Jack", "Ben", null, null, ..., null }. A foreach statement later iterates each item including null values, and it tries to call method on a null object, so you get NullReferenceException.
May be you should better use List<string> instead of array to avoid such kind of problems. Call Add() to add items, and Clear() method before you enter students again(to get free of students from previous "session"). And when you later iterate this collection with a foreach loop everything will be ok.
I posted 2 solutions in your other question about this method in this program that accounts for the array size issues in two different ways. Use int.TryParse instead of convert and you either need to resize the array based on user input on the number of students or you need to check for nulls in each iteration of the answer array, skipping nulls. See the example code i provided in your other question.

How would you make this switch statement as fast as possible?

2009-12-04 UPDATE: For profiling results on a number of the suggestions posted here, see below!
The Question
Consider the following very harmless, very straightforward method, which uses a switch statement to return a defined enum value:
public static MarketDataExchange GetMarketDataExchange(string ActivCode) {
if (ActivCode == null) return MarketDataExchange.NONE;
switch (ActivCode) {
case "": return MarketDataExchange.NBBO;
case "A": return MarketDataExchange.AMEX;
case "B": return MarketDataExchange.BSE;
case "BT": return MarketDataExchange.BATS;
case "C": return MarketDataExchange.NSE;
case "MW": return MarketDataExchange.CHX;
case "N": return MarketDataExchange.NYSE;
case "PA": return MarketDataExchange.ARCA;
case "Q": return MarketDataExchange.NASDAQ;
case "QD": return MarketDataExchange.NASDAQ_ADF;
case "W": return MarketDataExchange.CBOE;
case "X": return MarketDataExchange.PHLX;
case "Y": return MarketDataExchange.DIRECTEDGE;
}
return MarketDataExchange.NONE;
}
My colleague and I batted around a few ideas today about how to actually make this method faster, and we came up with some interesting modifications that did in fact improve its performance rather significantly (proportionally speaking, of course). I'd be interested to know what sorts of optimizations anyone else out there can think up that might not have occurred to us.
Right off the bat, let me just offer a quick disclaimer: this is for fun, and not to fuel the whole "to optimize or not to optimize" debate. That said, if you count yourself among those who dogmatically believe "premature optimization is the root of all evil," just be aware that I work for a high-frequency trading firm, where everything needs to run absolutely as fast as possible--bottleneck or not. So, even though I'm posting this on SO for fun, it isn't just a huge waste of time, either.
One more quick note: I'm interested in two kinds of answers--those that assume every input will be a valid ActivCode (one of the strings in the switch statement above), and those that do not. I am almost certain that making the first assumption allows for further speed improvements; anyway, it did for us. But I know that improvements are possible either way.
The Results
Well, it turns out that the fastest solution so far (that I've tested) came from João Angelo, whose suggestion was actually very simple, yet extremely clever. The solution that my coworker and I had devised (after trying out several approaches, many of which were thought up here as well) came in second place; I was going to post it, but it turns out that Mark Ransom came up with the exact same idea, so just check out his answer!
Since I ran these tests, some other users have posted even newer ideas... I will test them in due time, when I have a few more minutes to spare.
I ran these tests on two different machines: my personal computer at home (a dual-core Athlon with 4 Gb RAM running Windows 7 64-bit) and my development machine at work (a dual-core Athlon with 2 Gb RAM running Windows XP SP3). Obviously, the times were different; however, the relative times, meaning, how each method compared to every other method, were the same. That is to say, the fastest was the fastest on both machines, etc.
Now for the results. (The times I'm posting below are from my home computer.)
But first, for reference--the original switch statement:
1000000 runs: 98.88 ms
Average: 0.09888 microsecond
Fastest optimizations so far:
João Angelo's idea of assigning values to the enums based on the hash codes of the ActivCode strings and then directly casing ActivCode.GetHashCode() to MarketDataExchange:
1000000 runs: 23.64 ms
Average: 0.02364 microsecond
Speed increase: 329.90%
My colleague's and my idea of casting ActivCode[0] to an int and retrieving the appropriate MarketDataExchange from an array initialized on startup (this exact same idea was suggested by Mark Ransom):
1000000 runs: 28.76 ms
Average: 0.02876 microsecond
Speed increase: 253.13%
tster's idea of switching on the output of ActivCode.GetHashCode() instead of ActivCode:
1000000 runs: 34.69 ms
Average: 0.03469 microsecond
Speed increase: 185.04%
The idea, suggested by several users including Auraseer, tster, and kyoryu, of switching on ActivCode[0] instead of ActivCode:
1000000 runs: 36.57 ms
Average: 0.03657 microsecond
Speed increase: 174.66%
Loadmaster's idea of using the fast hash, ActivCode[0] + ActivCode[1]*0x100:
1000000 runs: 39.53 ms
Average: 0.03953 microsecond
Speed increase: 153.53%
Using a hashtable (Dictionary<string, MarketDataExchange>), as suggested by many:
1000000 runs: 88.32 ms
Average: 0.08832 microsecond
Speed increase: 12.36%
Using a binary search:
1000000 runs: 1031 ms
Average: 1.031 microseconds
Speed increase: none (performance worsened)
Let me just say that it has been really cool to see how many different ideas people had on this simple problem. This was very interesting to me, and I'm quite thankful to everyone who has contributed and made a suggestion so far.
Assuming every input will be a valid ActivCode, that you can change the enumeration values and highly coupled to the GetHashCode implementation:
enum MarketDataExchange
{
NONE,
NBBO = 371857150,
AMEX = 372029405,
BSE = 372029408,
BATS = -1850320644,
NSE = 372029407,
CHX = -284236702,
NYSE = 372029412,
ARCA = -734575383,
NASDAQ = 372029421,
NASDAQ_ADF = -1137859911,
CBOE = 372029419,
PHLX = 372029430,
DIRECTEDGE = 372029429
}
public static MarketDataExchange GetMarketDataExchange(string ActivCode)
{
if (ActivCode == null) return MarketDataExchange.NONE;
return (MarketDataExchange)ActivCode.GetHashCode();
}
I'd roll my own fast hash function and use an integer switch statement to avoid string comparisons:
int h = 0;
// Compute fast hash: A[0] + A[1]*0x100
if (ActivCode.Length > 0)
h += (int) ActivCode[0];
if (ActivCode.Length > 1)
h += (int) ActivCode[1] << 8;
// Find a match
switch (h)
{
case 0x0000: return MarketDataExchange.NBBO; // ""
case 0x0041: return MarketDataExchange.AMEX; // "A"
case 0x0042: return MarketDataExchange.BSE; // "B"
case 0x5442: return MarketDataExchange.BATS; // "BT"
case 0x0043: return MarketDataExchange.NSE; // "C"
case 0x574D: return MarketDataExchange.CHX; // "MW"
case 0x004E: return MarketDataExchange.NYSE; // "N"
case 0x4150: return MarketDataExchange.ARCA; // "PA"
case 0x0051: return MarketDataExchange.NASDAQ; // "Q"
case 0x4451: return MarketDataExchange.NASDAQ_ADF; // "QD"
case 0x0057: return MarketDataExchange.CBOE; // "W"
case 0x0058: return MarketDataExchange.PHLX; // "X"
case 0x0059: return MarketDataExchange.DIRECTEDGE; // "Y"
default: return MarketDataExchange.NONE;
}
My tests show that this is about 4.5 times faster than the original code.
If C# had a preprocessor, I'd use a macro to form the case constants.
This technique is faster than using a hash table and certainly faster than using string comparisons. It works for up to four-character strings with 32-bit ints, and up to 8 characters using 64-bit longs.
If you know how often the various codes show up, the more common ones should go at the top of the list, so fewer comparisons are done. But let's assume you don't have that.
Assuming the ActivCode is always valid will of course speed things up. You don't need to test for null or the empty string, and you can leave off one test from the end of the switch. That is, test for everything except Y, and then return DIRECTEDGE if no match is found.
Instead of switching on the whole string, switch on its first letter. For the codes that have more letters, put a second test inside the switch case. Something like this:
switch(ActivCode[0])
{
//etc.
case 'B':
if ( ActivCode.Length == 1 ) return MarketDataExchange.BSE;
else return MarketDataExchange.BATS;
// etc.
}
It would be better if you could go back and change the codes so they are all single characters, because you would then never need more than one test. Better yet would be using the numerical value of the enum, so you can simply cast instead of having to switch/translate in the first place.
I'd use a dictionary for the key value pairs and take advantage of the O(1) lookup time.
Do you have any statistics on which strings are more common? So that those could be checked first?
With a valid input could use
if (ActivCode.Length == 0)
return MarketDataExchange.NBBO;
if (ActivCode.Length == 1)
return (MarketDataExchange) (ActivCode[0]);
return (MarketDataExchange) (ActivCode[0] | ActivCode[1] << 8);
Change the switch to switch on the HashCode() of the strings.
I'd extrapolate tster's reply to "switch over a custom hash function", assuming that the code generator creates a lookup table, or - failing that - building the lookup table myself.
The custom hash function should be simple, e.g.:
(int)ActivCode[0]*2 + ActivCode.Length-1
This would require a table of 51 elements, easily kept in L1 cache, under the following assumptions:
Input data must already be validated
empty string must be handled sepsarately
no two-character-codes start with the same character
adding new cases is hard
the empty string case could be incorporated if you could use an unsafe access to ActivCode[0] yielding the '\0' terminator.
Forgive me if I get something wrong here, I'm extrapolating from my knowledge of C++. For example, if you take ActivCode[0] of an empty string, in C++ you get a character whose value is zero.
Create a two dimensional array which you initialize once; the first dimension is the length of the code, the second is a character value. Populate with the enumeration value you'd like to return. Now your entire function becomes:
public static MarketDataExchange GetMarketDataExchange(string ActivCode) {
return LookupTable[ActivCode.Length][ActivCode[0]];
}
Lucky for you all the two-character codes are unique in the first letter compared to the other two-character codes.
I would put it in dictionary instead of using a switch statement. That being said, it may not make a difference. Or it might. See C# switch statement limitations - why?.
Avoid all string comparisons.
Avoid looking at more than a single character (ever)
Avoid if-else since I want the compiler to be able optimize the best it can
Try to get the result in a single switch jump
code:
public static MarketDataExchange GetMarketDataExchange(string ActivCode) {
if (ActivCode == null) return MarketDataExchange.NONE;
int length = ActivCode.Length;
if (length == 0) return MarketDataExchange.NBBO;
switch (ActivCode[0]) {
case 'A': return MarketDataExchange.AMEX;
case 'B': return (length == 2) ? MarketDataExchange.BATS : MarketDataExchange.BSE;
case 'C': return MarketDataExchange.NSE;
case 'M': return MarketDataExchange.CHX;
case 'N': return MarketDataExchange.NYSE;
case 'P': return MarketDataExchange.ARCA;
case 'Q': return (length == 2) ? MarketDataExchange.NASDAQ_ADF : MarketDataExchange.NASDAQ;
case 'W': return MarketDataExchange.CBOE;
case 'X': return MarketDataExchange.PHLX;
case 'Y': return MarketDataExchange.DIRECTEDGE;
default: return MarketDataExchange.NONE;
}
}
Trade memory for speed by pre-populating an index table to leverage simple pointer arithmetic.
public class Service
{
public static MarketDataExchange GetMarketDataExchange(string ActivCode) {
{
int x = 65, y = 65;
switch(ActivCode.Length)
{
case 1:
x = ActivCode[0];
break;
case 2:
x = ActivCode[0];
y = ActivCode[1];
break;
}
return _table[x, y];
}
static Service()
{
InitTable();
}
public static MarketDataExchange[,] _table =
new MarketDataExchange['Z','Z'];
public static void InitTable()
{
for (int x = 0; x < 'Z'; x++)
for (int y = 0; y < 'Z'; y++)
_table[x, y] = MarketDataExchange.NONE;
SetCell("", MarketDataExchange.NBBO);
SetCell("A", MarketDataExchange.AMEX);
SetCell("B", MarketDataExchange.BSE);
SetCell("BT", MarketDataExchange.BATS);
SetCell("C", MarketDataExchange.NSE);
SetCell("MW", MarketDataExchange.CHX);
SetCell("N", MarketDataExchange.NYSE);
SetCell("PA", MarketDataExchange.ARCA);
SetCell("Q", MarketDataExchange.NASDAQ);
SetCell("QD", MarketDataExchange.NASDAQ_ADF);
SetCell("W", MarketDataExchange.CBOE);
SetCell("X", MarketDataExchange.PHLX);
SetCell("Y", MarketDataExchange.DIRECTEDGE);
}
private static void SetCell(string s, MarketDataExchange exchange)
{
char x = 'A', y = 'A';
switch(s.Length)
{
case 1:
x = s[0];
break;
case 2:
x = s[0];
y = s[1];
break;
}
_table[x, y] = exchange;
}
}
Make the enum byte-based to save a little space.
public enum MarketDataExchange : byte
{
NBBO, AMEX, BSE, BATS, NSE, CHX, NYSE, ARCA,
NASDAQ, NASDAQ_ADF, CBOE, PHLIX, DIRECTEDGE, NONE
}
If the enumeration values are arbitrary you could do this...
public static MarketDataExchange GetValue(string input)
{
switch (input.Length)
{
case 0: return MarketDataExchange.NBBO;
case 1: return (MarketDataExchange)input[0];
case 2: return (MarketDataExchange)(input[0] << 8 | input[1]);
default: return MarketDataExchange.None;
}
}
... if you want to go totally nuts you can also use an unsafe call with pointers as noted by Pavel Minaev ...
The pure cast version above is faster than this unsafe version.
unsafe static MarketDataExchange GetValue(string input)
{
if (input.Length == 1)
return (MarketDataExchange)(input[0]);
fixed (char* buffer = input)
return (MarketDataExchange)(buffer[0] << 8 | buffer[1]);
}
public enum MarketDataExchange
{
NBBO = 0x00, //
AMEX = 0x41, //A
BSE = 0x42, //B
BATS = 0x4254, //BT
NSE = 0x43, //C
CHX = 0x4D57, //MW
NYSE = 0x4E, //N
ARCA = 0x5041, //PA
NASDAQ = 0x51, //Q
NASDAQ_ADF = 0x5144, //QD
CBOE = 0x57, //W
PHLX = 0x58, //X
DIRECTEDGE = 0x59, //Y
None = -1
}
+1 for using a dictionary. Not necessarily for optimization, but it'd be cleaner.
I would probably use constants for the strings as well, though i doubt that'd buy you anything performance wise.
Messy but using a combination of nested ifs and hard coding might just beat the optimiser:-
if (ActivCode < "N") {
// "" to "MW"
if (ActiveCode < "BT") {
// "" to "B"
if (ActiveCode < "B") {
// "" or "A"
if (ActiveCode < "A") {
// must be ""
retrun MarketDataExchange.NBBO;
} else {
// must be "A"
return MarketDataExchange.AMEX;
}
} else {
// must be "B"
return MarketDataExchange.BSE;
}
} else {
// "BT" to "MW"
if (ActiveCode < "MW") {
// "BT" or "C"
if (ActiveCode < "C") {
// must be "BT"
retrun MarketDataExchange.NBBO;
} else {
// must be "C"
return MarketDataExchange.NSE;
}
} else {
// must be "MV"
return MarketDataExchange.CHX;
}
}
} else {
// "N" TO "Y"
if (ActiveCode < "QD") {
// "N" to "Q"
if (ActiveCode < "Q") {
// "N" or "PA"
if (ActiveCode < "PA") {
// must be "N"
retrun MarketDataExchange.NYSE;
} else {
// must be "PA"
return MarketDataExchange.ARCA;
}
} else {
// must be "Q"
return MarketDataExchange.NASDAQ;
}
} else {
// "QD" to "Y"
if (ActiveCode < "X") {
// "QD" or "W"
if (ActiveCode < "W") {
// must be "QD"
retrun MarketDataExchange.NASDAQ_ADF;
} else {
// must be "W"
return MarketDataExchange.CBOE;
}
} else {
// "X" or "Y"
if (ActiveCode < "Y") {
// must be "X"
retrun MarketDataExchange.PHLX;
} else {
// must be "Y"
return MarketDataExchange.DIRECTEDGE;
}
}
}
}
This gets the right function with three or four compares. I wouldnt even think of doing this for real unless your piece of code is expected to run several times a second!
You further otimise it so that only single character compares occurred.
e.g. replace '< "BT" ' with '>= "B" ' -- ever so slightly faster and even less readable!
All your strings are at most 2 chars long, and ASCII, so we can use 1 byte per char.
Furthermore, more likely than not, they also never can have \0 appear in them (.NET string allows for embedded null characters, but many other things don't). With that assumption, we can null-pad all your strings to be exactly 2 bytes each, or an ushort:
"" -> (byte) 0 , (byte) 0 -> (ushort)0x0000
"A" -> (byte)'A', (byte) 0 -> (ushort)0x0041
"B" -> (byte)'B', (byte) 0 -> (ushort)0x0042
"BT" -> (byte)'B', (byte)'T' -> (ushort)0x5442
Now that we have a single integer in a relatively (64K) short range, we can use a lookup table:
MarketDataExchange[] lookup = {
MarketDataExchange.NBBO,
MarketDataExchange.NONE,
MarketDataExchange.NONE,
...
/* at index 0x041 */
MarketDataExchange.AMEX,
MarketDataExchange.BSE,
MarketDataExchange.NSE,
...
};
Now, obtaining the value given a string is:
public static unsafe MarketDataExchange GetMarketDataExchange(string s)
{
// Assume valid input
if (s.Length == 0) return MarketDataExchange.NBBO;
// .NET strings always have '\0' after end of data - abuse that
// to avoid extra checks for 1-char strings. Skip index checks as well.
ushort hash;
fixed (char* data = s)
{
hash = (ushort)data[0] | ((ushort)data[1] << 8);
}
return lookup[hash];
}
Put the cases in a sorted structure with non linear access (like a hash table).
The switch that you have will have a linear time.
You can get a mild speed-up by ordering the codes according to which ones are most used.
But I agree with Cletus: the best speed-up I can think of would be to use a hash map with plenty of room (so that there are no collisions.)
A couple of random thoughts, that may not all be applicable together:
Switch on the first character in the string, rather than the string itself, and do a sub-switch for strings which can contain more than one letter?
A hashtable would certainly guarantee O(1) retrieval, though it might not be faster for smaller numbers of comparisons.
Don't use strings, use enums or something like a flyweight instead. Using strings in this case seems a bit fragile anyway...
And if you really need it to be as fast as possible, why aren't you writing it in assembly? :)
Can we cast the ActivCode to int and then use int in our case statements?
Use the length of the code to create a unique value from that code instead of using GetHashCode() . It turns out there are no collisions if you use the first letter of the code shifted by the length of the code. This reduces the cost to two comparisons, one array index and one shift (on average).
public static MarketDataExchange GetMarketDataExchange(string ActivCode)
{
if (ActivCode == null)
return MarketDataExchange.NONE;
if (ActivCode.Length == 0)
return MarketDataExchange.NBBO;
return (MarketDataExchange)((ActivCode[0] << ActivCode.Length));
}
public enum MarketDataExchange
{
NONE = 0,
NBBO = 1,
AMEX = ('A'<<1),
BSE = ('B'<<1),
BATS = ('B'<<2),
NSE = ('C'<<1),
CHX = ('M'<<2),
NYSE = ('N'<<1),
ARCA = ('P'<<2),
NASDAQ = ('Q'<<1),
NASDAQ_ADF = ('Q'<<2),
CBOE = ('W'<<1),
PHLX = ('X'<<1),
DIRECTEDGE = ('Y'<<1),
}

Convert String to int in C#

I am trying to write a simple program that asks the user to enter a number and then I will use that number to decide what the cost of the ticket will be for their given age. I am having trouble when trying to convert the string to int. Otherwise the program layout is fine. Any suggestions?
thanks
using System;
class ticketPrice
{
public static void Main(String[] args)
{
Console.WriteLine("Please Enter Your Age");
int input = Console.ReadLine();
if (input < 5)
{
Console.WriteLine("You are "+input+" and the admisson is FREE!");
}
else if (input > 4 & input < 18)
{
Console.WriteLine("You are "+input+" and the admission is $5");
}
else if (input > 17 & input < 56)
{
Console.WriteLine("You are "+input+" and the admission is $10");
}
else if (input > 55)
{
Console.WriteLine("You are "+input+" and the admission is $8");
}
}
}
Try the int.TryParse(...) method. It doesn't throw an exception.
http://msdn.microsoft.com/en-us/library/f02979c7.aspx
Also, you should use && not & in your conditions. && is logical AND and & is bitwise AND.
For easy parsing of strings to integers (and other number types), use that number type's .TryParse(inputstring, yourintegervariable) method. This method will output a Boolean (True/False), letting you know whether the operation passed or failed. If the result is false, you can give an error message before going any further (don't have to worry about crashing your program).
Previous text concerning switch statements has been removed
In C#, you need to use the && operator for logical AND. & is not the same and may not work the way you believe it will.
I suggest to use the Int32.TryParse() method. Further I suggest to refactor your code - you can make it much cleaner (assuming this is not just example code). One solution is to use a key value pair list to map from age to admission.
using System;
using System.Collections.Generic;
using System.Linq;
static class TicketPrice
{
private static readonly IList<KeyValuePair<Int32, String>> AgeAdmissionMap =
new List<KeyValuePair<Int32, String>>
{
new KeyValuePair<Int32, String>(0, "FREE!"),
new KeyValuePair<Int32, String>(5, "$5."),
new KeyValuePair<Int32, String>(18, "$10."),
new KeyValuePair<Int32, String>(56, "$8.")
};
public static void Main(String[] args)
{
Console.WriteLine("Please Enter Your Age!");
UInt32 age;
while (!UInt32.TryParse(Console.ReadLine(), out age)) { }
String admission = TicketPrice.AgeAdmissionMap
.OrderByDescending(pair => pair.Key)
.First(pair => pair.Key <= age)
.Value;
Console.WriteLine(String.Format(
"You are {0} and the admission is {1}",
age,
admission));
}
}
I used an unsigned integer to prevent entering negative ages and put the input into a loop. This way the user can correct an invalid input.
int number = int.Parse(Console.ReadLine());
Be aware that this will throw an exception if they enter an invalid number.
The first thing you need to do is change your input variable to a string:
string input = Console.ReadLine();
Once you have that, there are several ways to convert it to an integer. See this answer for more info:
Better way to cast object to int

Categories

Resources