私はT-SQLの初心者です。入力文字列が回文かどうかを判断したいのですが、そうでない場合は出力= 0、そうであれば出力= 1となります。私はまだ構文を理解しています。エラーメッセージも表示されません。 T-SQLがどのように機能するかをよりよく理解して知識を得るために、さまざまなソリューションといくつかのフィードバックを探しています。
私が見るように、重要なアイデアは、左と右のほとんどの文字を互いに比較して、等しいかどうかを確認し、次に左から2番目の文字を最後から2番目の文字と比較することです。ループを行います。文字が互いに等しい場合は、続行します。最後に達した場合は1を出力し、そうでない場合は0を出力します。
批評をお願いします:
CREATE function Palindrome(
@String Char
, @StringLength Int
, @n Int
, @Palindrome BIN
, @StringLeftLength Int
)
RETURNS Binary
AS
BEGIN
SET @ n=1
SET @StringLength= Len(String)
WHILE @StringLength - @n >1
IF
Left(String,@n)=Right(String, @StringLength)
SET @n =n+1
SET @StringLength =StringLength -1
RETURN @Binary =1
ELSE RETURN @Palindrome =0
END
私は正しい方向に進んでいると思いますが、まだ長い道のりです。何か案は?
かなりの数の解決策があるので、私はあなたの質問の「批評」の部分に行きます。いくつかのメモ:タイプミスを修正し、どこに修正したかを書き留めました。それらがタイプミスであることについて私が間違っている場合は、コメントでそれを言及し、何が起こっているのかを説明します。既にご存じかもしれないことをいくつか指摘しておきますので、気にせずに怒らないでください。いくつかのコメントはうるさいように見えるかもしれませんが、私はあなたがあなたの旅のどこにいるのかわからないので、あなたがあなたが始まったばかりだと仮定しなければなりません。
_CREATE function Palindrome (
@String Char
, @StringLength Int
, @n Int
, @Palindrome BIN
, @StringLeftLength Int
_
[〜#〜]常に[〜#〜]は、char
またはvarchar
の定義に長さを含めます。 Aaron Bertrand ここで詳しく説明します 。彼はvarchar
について話しているが、char
についても同じことが言えます。比較的短い文字列だけが必要な場合はvarchar(255)
を使用します。より大きな文字列またはvarchar(8000)
の場合はvarchar(max)
を使用します。 Varchar
は可変長文字列用ですchar
は固定文字列用です。渡される文字列の長さがわからないため、varchar
を使用してください。また、binary
ではなくbin
です。
次に、これらの変数のすべてをパラメーターとして配置する必要はありません。コード内で宣言します。渡すか、渡すかを計画している場合にのみ、パラメーターリストに何かを入れてください。 (これが最後にどのように見えるかがわかります。)また、@ StringLeftLengthがありますが、使用しないでください。だから私はそれを宣言するつもりはありません。
次に行うことは、いくつかを明確にするために少し再フォーマットすることです。
_BEGIN
SET @n=1
SET @StringLength = Len(@String) -- Missed an @
WHILE @StringLength - @n >1
IF Left(@String,@n)=Right(@String, @StringLength) -- More missing @s
SET @n = @n + 1 -- Another missing @
SET @StringLength = @StringLength - 1 -- Watch those @s :)
RETURN @Palindrome = 1 -- Assuming another typo here
ELSE
RETURN @Palindrome =0
END
_
私がインデントした方法を見ると、私がこれを持っていることに気付くでしょう:
_ WHILE @StringLength - @n >1
IF Left(@String,@n)=Right(@String, @StringLength)
SET @n = @n + 1
_
これは、WHILE
やIF
のようなコマンドは、それらの後のコードの最初の行にのみ影響を与えるためです。複数のコマンドが必要な場合は、_BEGIN .. END
_ブロックを使用する必要があります。したがって、次のように修正します。
_ WHILE @StringLength - @n > 1
IF Left(@String,@n)=Right(@String, @StringLength)
BEGIN
SET @n = @n + 1
SET @StringLength = @StringLength - 1
RETURN @Palindrome = 1
END
ELSE
RETURN @Palindrome = 0
_
IF
に_BEGIN .. END
_ブロックを追加しただけであることがわかります。これは、IF
ステートメントが複数行であっても(複数のコマンドが含まれていても)単一のステートメントであるためです(IF
およびELSE
部分で実行されたすべてをカバーしています)ステートメントの)。
次に、両方のRETURNs
の後にエラーが発生します。変数を返すことができますORリテラル。変数を設定して同時に返すことはできません。
_ SET @Palindrome = 1
END
ELSE
SET @Palindrome = 0
RETURN @Palindrome
_
今、私たちは論理に入りました。最初に、使用しているLEFT
関数とRIGHT
関数は優れていることを指摘しておきますが、これらの関数は、要求された方向から渡した文字数を提供します。それでは、「テスト」という言葉に合格したとします。最初のパスでは、これを取得します(変数を削除します)。
_LEFT('test',1) = RIGHT('test',4)
t = test
LEFT('test',2) = RIGHT('test',3)
te = est
_
明らかにそれはあなたが期待したものではありません。代わりにsubstring
を実際に使用する必要があります。サブストリングを使用すると、開始点だけでなく長さも渡すことができます。だからあなたは得るでしょう:
_SUBSTRING('test',1,1) = SUBSTRING('test',4,1)
t = t
SUBSTRING('test',2,1) = SUBSTRING('test',3,1)
e = s
_
次に、IFステートメントの1つの条件でのみ、ループで使用する変数をインクリメントします。変数の増分をその構造から完全に引き出します。これには追加の_BEGIN .. END
_ブロックが必要になりますが、他のブロックは削除します。
_ WHILE @StringLength - @n > 1
BEGIN
IF SUBSTRING(@String,@n,1) = SUBSTRING(@String, @StringLength,1)
SET @Palindrome = 1
ELSE
SET @Palindrome = 0
SET @n = @n + 1
SET @StringLength = @StringLength - 1
END
_
最後のテストを許可するには、WHILE
条件を変更する必要があります。
_ WHILE @StringLength > @n
_
最後に重要なことですが、現在の状態では、奇数の文字がある場合、最後の文字をテストしません。たとえば、「ana」の場合、n
はテストされません。それで結構ですが、1文字のWordを説明する必要があります(肯定的なものとしてカウントする場合)。したがって、前もって値を設定することでそれを行うことができます。
そして今、私たちはついに持っています:
_CREATE FUNCTION Palindrome (@String varchar(255))
RETURNS Binary
AS
BEGIN
DECLARE @StringLength Int
, @n Int
, @Palindrome binary
SET @n = 1
SET @StringLength = Len(@String)
SET @Palindrome = 1
WHILE @StringLength > @n
BEGIN
IF SUBSTRING(@String,@n,1) = SUBSTRING(@String, @StringLength,1)
SET @Palindrome = 1
ELSE
SET @Palindrome = 0
SET @n = @n + 1
SET @StringLength = @StringLength - 1
END
RETURN @Palindrome
END
_
最後のコメント。私は一般的にフォーマットの大ファンです。コードがどのように機能するかを確認し、起こり得る間違いを指摘するのに役立ちます。
編集
Sphinxxxで述べたように、論理にはまだ欠陥があります。 ELSE
を押して_@Palindrome
_を0に設定したら、続行しても意味がありません。実際、その時点でRETURN
だけで済みます。
_ IF SUBSTRING(@String,@n,1) = SUBSTRING(@String, @StringLength,1)
SET @Palindrome = 1
ELSE
RETURN 0
_
現在、「これはパリンドロームである可能性はまだある」ために_@Palindrome
_のみを使用していることを考えると、実際にそれを使用しても意味がありません。変数を取り除き、失敗した場合にロジックをshort circuitに切り替えます(_RETURN 0
_)および_RETURN 1
_(肯定的な応答)。ループ全体に渡ります。これにより、実際にロジックが多少簡略化されていることに気付くでしょう。
_CREATE FUNCTION Palindrome (@String varchar(255))
RETURNS Binary
AS
BEGIN
DECLARE @StringLength Int
, @n Int
SET @n = 1
SET @StringLength = Len(@String)
WHILE @StringLength > @n
BEGIN
IF SUBSTRING(@String,@n,1) <> SUBSTRING(@String, @StringLength,1)
RETURN 0
SET @n = @n + 1
SET @StringLength = @StringLength - 1
END
RETURN 1
END
_
Numbersテーブルアプローチを使用することもできます。
補助番号テーブルがまだない場合は、次のように作成できます。これには100万行が入力されるため、最大200万文字の文字列に適しています。
CREATE TABLE dbo.Numbers (number int PRIMARY KEY);
INSERT INTO dbo.Numbers
(number)
SELECT TOP 1000000 ROW_NUMBER() OVER (ORDER BY @@SPID)
FROM master..spt_values v1,
master..spt_values v2
以下は、左側の各文字を右側の対応するパートナーと比較し、不一致が見つかった場合、短絡して0を返す可能性があります。文字列が奇数の長さの場合、中央の文字はチェックされません。これは結果を変更しないためです。 。
DECLARE @Candidate VARCHAR(MAX) = 'aibohphobia'; /*the irrational fear of palindromes.*/
SET @Candidate = LTRIM(RTRIM(@Candidate)); /*Ignoring any leading or trailing spaces.
Could use `DATALENGTH` instead of `LEN` if these are significant*/
SELECT CASE
WHEN EXISTS (SELECT *
FROM dbo.Numbers
WHERE number <= LEN(@Candidate) / 2
AND SUBSTRING(@Candidate, number, 1)
<> SUBSTRING(@Candidate, 1 + LEN(@Candidate) - number, 1))
THEN 0
ELSE 1
END AS IsPalindrome
どのように機能するかわからない場合は、以下から確認できます
DECLARE @Candidate VARCHAR(MAX) = 'this is not a palindrome';
SELECT SUBSTRING(@Candidate, number, 1) AS [Left],
SUBSTRING(@Candidate, 1 + LEN(@Candidate) - number, 1) AS [Right]
FROM dbo.Numbers
WHERE number <= LEN(@Candidate) / 2;
これは基本的に質問で説明したのと同じアルゴリズムですが、反復的な手続き型コードではなく、セットベースの方法で行われます。
REVERSE
を使用せずに、すぐに頭に浮かぶが、それでもfunctionを使用する1;次のようなものを構築します。
この部分は、既存の関数が既に存在する場合、それを単に削除しました。
IF OBJECT_ID('dbo.IsPalindrome') IS NOT NULL
DROP FUNCTION dbo.IsPalindrome;
GO
これは関数自体です:
CREATE FUNCTION dbo.IsPalindrome
(
@Word NVARCHAR(500)
)
RETURNS BIT
AS
BEGIN
DECLARE @IsPalindrome BIT;
DECLARE @LeftChunk NVARCHAR(250);
DECLARE @RightChunk NVARCHAR(250);
DECLARE @StrLen INT;
DECLARE @Pos INT;
SET @RightChunk = '';
SET @IsPalindrome = 0;
SET @StrLen = LEN(@Word) / 2;
IF @StrLen % 2 = 1 SET @StrLen = @StrLen - 1;
SET @Pos = LEN(@Word);
SET @LeftChunk = LEFT(@Word, @StrLen);
WHILE @Pos > (LEN(@Word) - @StrLen)
BEGIN
SET @RightChunk = @RightChunk + SUBSTRING(@Word, @Pos, 1)
SET @Pos = @Pos - 1;
END
IF @LeftChunk = @RightChunk SET @IsPalindrome = 1;
RETURN (@IsPalindrome);
END
GO
ここでは、関数をテストします。
IF dbo.IsPalindrome('This is a Word') = 1
PRINT 'YES'
ELSE
PRINT 'NO';
IF dbo.IsPalindrome('tattarrattat') = 1
PRINT 'YES'
ELSE
PRINT 'NO';
これは、Wordの前半と後半の逆を比較します(REVERSE
関数を使用しない場合)。このコードは、奇数長と偶数長の両方のワードを適切に処理します。 Word全体をループする代わりに、Wordの前半のLEFT
を取得し、Wordの後半をループして右半分の逆の部分を取得します。 Wordが奇数の長さの場合、中間の文字はスキップします。これは、定義上、両方の「半分」で同じになるためです。
1-関数が非常に遅くなる可能性があります!
これはインラインTVF対応バージョンの Martin Smithのセットベースのソリューション であり、さらにいくつかの余分な機能強化が施されています。
WITH Nums AS
(
SELECT
N = number
FROM
dbo.Numbers WITH(FORCESEEK) /*Requires a suitably indexed numbers table*/
)
SELECT
IsPalindrome =
CASE
WHEN EXISTS
(
SELECT *
FROM Nums
WHERE N <= L / 2
AND SUBSTRING(S, N, 1) <> SUBSTRING(S, 1 + L - N, 1)
)
THEN 0
ELSE 1
END
FROM
(SELECT LTRIM(RTRIM(@Candidate)), LEN(@Candidate)) AS v (S, L)
;
REVERSEを使用せずに...再帰的なソリューションを使用することは常に楽しいです;)(SQL Server 2012で採掘しましたが、以前のバージョンでは再帰に制限がある可能性があります)
create function dbo.IsPalindrome (@s varchar(max)) returns bit
as
begin
return case when left(@s,1) = right(@s,1) then case when len(@s) < 3 then 1 else dbo.IsPalindrome(substring(@s,2,len(@s)-2)) end else 0 end;
end;
GO
select dbo.IsPalindrome('a')
1
select dbo.IsPalindrome('ab')
0
select dbo.IsPalindrome('bab')
1
select dbo.IsPalindrome('gohangasalamiimalasagnahog')
1
楽しさのために、インメモリOLTP機能を備えたSQL Server 2016スカラーユーザー定義関数を次に示します。
ALTER FUNCTION dbo.IsPalindrome2 ( @inputString NVARCHAR(500) )
RETURNS BIT
WITH NATIVE_COMPILATION, SCHEMABINDING
AS
BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'English')
DECLARE @i INT = 1, @j INT = LEN(@inputString)
WHILE @i < @j
BEGIN
IF SUBSTRING( @inputString, @i, 1 ) != SUBSTRING( @inputString, @j, 1 )
BEGIN
RETURN(0)
END
ELSE
SELECT @i+=1, @j-=1
END
RETURN(1)
END
GO
遭遇する主な問題は、1より大きい値を指定すると、LEFT
またはRIGHT
はその位置の文字ではなく複数の文字を返すことです。このテスト方法を使い続けたい場合、変更する本当に簡単な方法は
RIGHT(LEFT(String,@n),1)=LEFT(RIGHT(String, @StringLength),1)
これにより、常に左の文字列の右端の文字と右の文字列の左端の文字が取得されます。
おそらく、これを確認するためのそれほど遠くない方法は、SUBSTRING
を使用することです。
SUBSTRING(String, @n, 1) = SUBSTRING(String, ((LEN(String) - @n) + 1), 1)
SUBSTRING
は1インデックスなので、+ 1
in ((LEN(String) - @n) + 1)
。